/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Rhino code, released
 * May 6, 1999.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1997-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Patrick Beard
 *   Norris Boyd
 *   Igor Bukanov
 *   Mike Harm
 *   Ethan Hugg
 *   Bob Jervis
 *   Roger Lawrence
 *   Terry Lucas
 *   Frank Mitchell
 *   Milen Nankov
 *   Hannes Wallnoefer
 *   Andrew Wason
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU General Public License Version 2 or later (the "GPL"), in which
 * case the provisions of the GPL are applicable instead of those above. If
 * you wish to allow use of your version of this file only under the terms of
 * the GPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replacing
 * them with the notice and other provisions required by the GPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the GPL.
 *
 * ***** END LICENSE BLOCK ***** */

package org.mozilla.javascript;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

import org.mozilla.javascript.xml.XMLLib;
import org.mozilla.javascript.xml.XMLObject;

/**
 * This is the class that implements the runtime.
 * 
 * @author Norris Boyd
 */

public class ScriptRuntime {

  /*
   * OPT there's a noticable delay for the first error! Maybe it'd make sense to
   * use a ListResourceBundle instead of a properties file to avoid
   * (synchronized) text parsing.
   */
  private static class DefaultMessageProvider implements MessageProvider {
    public String getMessage(String messageId, Object[] arguments) {
      final String defaultResource = "org.mozilla.javascript.resources.Messages";

      Context cx = Context.getCurrentContext();
      Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();

      // ResourceBundle does caching.
      ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);

      String formatString;
      try {
        formatString = rb.getString(messageId);
      } catch (java.util.MissingResourceException mre) {
        throw new RuntimeException(
            "no message resource found for message property " + messageId);
      }

      /*
       * It's OK to format the string, even if 'arguments' is null; we need to
       * format it anyway, to make double ''s collapse to single 's.
       */
      MessageFormat formatter = new MessageFormat(formatString);
      return formatter.format(arguments);
    }
  }

  /**
   * This is the enumeration needed by the for..in statement.
   * 
   * See ECMA 12.6.3.
   * 
   * IdEnumeration maintains a ObjToIntMap to make sure a given id is enumerated
   * only once across multiple objects in a prototype chain.
   * 
   * XXX - ECMA delete doesn't hide properties in the prototype, but js/ref
   * does. This means that the js/ref for..in can avoid maintaining a hash table
   * and instead perform lookups to see if a given property has already been
   * enumerated.
   * 
   */
  private static class IdEnumeration implements Serializable {
    private static final long serialVersionUID = 1L;
    Scriptable obj;
    Object[] ids;
    int index;
    ObjToIntMap used;
    Object currentId;
    int enumType; /*
                   * one of ENUM_INIT_KEYS, ENUM_INIT_VALUES, ENUM_INIT_ARRAY
                   */

    // if true, integer ids will be returned as numbers rather than strings
    boolean enumNumbers;

    Scriptable iterator;
  }

  /**
   * This is an interface defining a message provider. Create your own
   * implementation to override the default error message provider.
   * 
   * @author Mike Harm
   */
  public interface MessageProvider {

    /**
     * Returns a textual message identified by the given messageId,
     * parameterized by the given arguments.
     * 
     * @param messageId
     *          the identifier of the message
     * @param arguments
     *          the arguments to fill into the message
     */
    String getMessage(String messageId, Object[] arguments);
  }

  static class NoSuchMethodShim implements Callable {
    String methodName;
    Callable noSuchMethodMethod;

    NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) {
      this.noSuchMethodMethod = noSuchMethodMethod;
      this.methodName = methodName;
    }
    /**
     * Perform the call.
     * 
     * @param cx
     *          the current Context for this thread
     * @param scope
     *          the scope to use to resolve properties.
     * @param thisObj
     *          the JavaScript <code>this</code> object
     * @param args
     *          the array of arguments
     * @return the result of the call
     */
    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
        Object[] args) {
      Object[] nestedArgs = new Object[2];

      nestedArgs[0] = methodName;
      nestedArgs[1] = newArrayLiteral(args, null, cx, scope);
      return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs);
    }

  }
  /*
   * There's such a huge space (and some time) waste for the Foo.class syntax:
   * the compiler sticks in a test of a static field in the enclosing class for
   * null and the code for creating the class value. It has to do this since the
   * reference has to get pushed off until execution time (i.e. can't force an
   * early load), but for the 'standard' classes - especially those in
   * java.lang, we can trust that they won't cause problems by being loaded
   * early.
   */
  public final static Class<?> BooleanClass = Kit
      .classOrNull("java.lang.Boolean"), ByteClass = Kit
      .classOrNull("java.lang.Byte"), CharacterClass = Kit
      .classOrNull("java.lang.Character"), ClassClass = Kit
      .classOrNull("java.lang.Class"), DoubleClass = Kit
      .classOrNull("java.lang.Double"), FloatClass = Kit
      .classOrNull("java.lang.Float"), IntegerClass = Kit
      .classOrNull("java.lang.Integer"), LongClass = Kit
      .classOrNull("java.lang.Long"), NumberClass = Kit
      .classOrNull("java.lang.Number"), ObjectClass = Kit
      .classOrNull("java.lang.Object"), ShortClass = Kit
      .classOrNull("java.lang.Short"), StringClass = Kit
      .classOrNull("java.lang.String"), DateClass = Kit
      .classOrNull("java.util.Date");

  public final static Class<?> ContextClass = Kit
      .classOrNull("org.mozilla.javascript.Context"), ContextFactoryClass = Kit
      .classOrNull("org.mozilla.javascript.ContextFactory"),
      FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"),
      ScriptableObjectClass = Kit
          .classOrNull("org.mozilla.javascript.ScriptableObject");

  public static final Class<Scriptable> ScriptableClass = Scriptable.class;

  private static final String[] lazilyNames = {"RegExp",
      "org.mozilla.javascript.regexp.NativeRegExp", "Packages",
      "org.mozilla.javascript.NativeJavaTopPackage", "java",
      "org.mozilla.javascript.NativeJavaTopPackage", "javax",
      "org.mozilla.javascript.NativeJavaTopPackage", "org",
      "org.mozilla.javascript.NativeJavaTopPackage", "com",
      "org.mozilla.javascript.NativeJavaTopPackage", "edu",
      "org.mozilla.javascript.NativeJavaTopPackage", "net",
      "org.mozilla.javascript.NativeJavaTopPackage", "getClass",
      "org.mozilla.javascript.NativeJavaTopPackage", "JavaAdapter",
      "org.mozilla.javascript.JavaAdapter", "JavaImporter",
      "org.mozilla.javascript.ImporterTopLevel",
      "Continuation",
      "org.mozilla.javascript.NativeContinuation",
      // TODO Grotesque hack using literal string (xml) just to minimize
      // changes for now
      "XML", "(xml)", "XMLList", "(xml)", "Namespace", "(xml)", "QName",
      "(xml)",};

  private static final Object LIBRARY_SCOPE_KEY = "LIBRARY_SCOPE";

  // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
  // versions 2.01 and 3.0P1, that causes some uses (returns at least) of
  // Double.NaN to be converted to 1.0.
  // So we use ScriptRuntime.NaN instead of Double.NaN.
  public static final double NaN = Double.longBitsToDouble(0x7ff8000000000000L);

  // A similar problem exists for negative zero.
  public static final double negativeZero = Double
      .longBitsToDouble(0x8000000000000000L);

  public static final Double NaNobj = new Double(NaN);

  /* Work around Microsoft Java VM bugs. */
  private final static boolean MSJVM_BUG_WORKAROUNDS = true;

  // XXX: this is until setDefaultNamespace will learn how to store NS
  // properly and separates namespace form Scriptable.get etc.
  private static final String DEFAULT_NS_TAG = "__default_namespace__";

  public static final int ENUMERATE_KEYS = 0;

  public static final int ENUMERATE_VALUES = 1;

  public static final int ENUMERATE_ARRAY = 2;

  public static final int ENUMERATE_KEYS_NO_ITERATOR = 3;

  public static final int ENUMERATE_VALUES_NO_ITERATOR = 4;

  public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5;

  public static MessageProvider messageProvider = new DefaultMessageProvider();

  public static final Object[] emptyArgs = new Object[0];

  public static final String[] emptyStrings = new String[0];

  public static Object add(Object val1, Object val2, Context cx) {
    if (val1 instanceof Number && val2 instanceof Number)
      return wrapNumber(((Number) val1).doubleValue()
          + ((Number) val2).doubleValue());
    if (val1 instanceof XMLObject) {
      Object test = ((XMLObject) val1).addValues(cx, true, val2);
      if (test != Scriptable.NOT_FOUND)
        return test;
    }
    if (val2 instanceof XMLObject) {
      Object test = ((XMLObject) val2).addValues(cx, false, val1);
      if (test != Scriptable.NOT_FOUND)
        return test;
    }
    if (val1 instanceof Scriptable)
      val1 = ((Scriptable) val1).getDefaultValue(null);
    if (val2 instanceof Scriptable)
      val2 = ((Scriptable) val2).getDefaultValue(null);
    if (!(val1 instanceof String) && !(val2 instanceof String))
      if (val1 instanceof Number && val2 instanceof Number)
        return wrapNumber(((Number) val1).doubleValue()
            + ((Number) val2).doubleValue());
      else
        return wrapNumber(toNumber(val1) + toNumber(val2));
    return toString(val1).concat(toString(val2));
  }

  public static String add(Object val1, String val2) {
    return toString(val1).concat(val2);
  }

  public static String add(String val1, Object val2) {
    return val1.concat(toString(val2));
  }

  public static void addInstructionCount(Context cx, int instructionsToAdd) {
    cx.instructionCount += instructionsToAdd;
    if (cx.instructionCount > cx.instructionThreshold) {
      cx.observeInstructionCount(cx.instructionCount);
      cx.instructionCount = 0;
    }
  }

  /**
   * Function.prototype.apply and Function.prototype.call
   * 
   * See Ecma 15.3.4.[34]
   */
  public static Object applyOrCall(boolean isApply, Context cx,
      Scriptable scope, Scriptable thisObj, Object[] args) {
    int L = args.length;
    Callable function = getCallable(thisObj);

    Scriptable callThis = null;
    if (L != 0)
      callThis = toObjectOrNull(cx, args[0]);
    if (callThis == null)
      // This covers the case of args[0] == (null|undefined) as well.
      callThis = getTopCallScope(cx);

    Object[] callArgs;
    if (isApply)
      // Follow Ecma 15.3.4.3
      callArgs = L <= 1 ? ScriptRuntime.emptyArgs : getApplyArguments(cx,
          args[1]);
    else // Follow Ecma 15.3.4.4
    if (L <= 1)
      callArgs = ScriptRuntime.emptyArgs;
    else {
      callArgs = new Object[L - 1];
      System.arraycopy(args, 1, callArgs, 0, L - 1);
    }

    return function.call(cx, scope, callThis, callArgs);
  }

  /**
   * Returns the object in the scope chain that has a given property.
   * 
   * The order of evaluation of an assignment expression involves evaluating the
   * lhs to a reference, evaluating the rhs, and then modifying the reference
   * with the rhs value. This method is used to 'bind' the given name to an
   * object containing that property so that the side effects of evaluating the
   * rhs do not affect which property is modified. Typically used in conjunction
   * with setName.
   * 
   * See ECMA 10.1.4
   */
  public static Scriptable bind(Context cx, Scriptable scope, String id) {
    Scriptable firstXMLObject = null;
    Scriptable parent = scope.getParentScope();
    childScopesChecks : if (parent != null) {
      // Check for possibly nested "with" scopes first
      while (scope instanceof NativeWith) {
        Scriptable withObj = scope.getPrototype();
        if (withObj instanceof XMLObject) {
          XMLObject xmlObject = (XMLObject) withObj;
          if (xmlObject.ecmaHas(cx, id))
            return xmlObject;
          if (firstXMLObject == null)
            firstXMLObject = xmlObject;
        } else if (ScriptableObject.hasProperty(withObj, id))
          return withObj;
        scope = parent;
        parent = parent.getParentScope();
        if (parent == null)
          break childScopesChecks;
      }
      for (;;) {
        if (ScriptableObject.hasProperty(scope, id))
          return scope;
        scope = parent;
        parent = parent.getParentScope();
        if (parent == null)
          break childScopesChecks;
      }
    }
    // scope here is top scope
    if (cx.useDynamicScope)
      scope = checkDynamicScope(cx.topCallScope, scope);
    if (ScriptableObject.hasProperty(scope, id))
      return scope;
    // Nothing was found, but since XML objects always bind
    // return one if found
    return firstXMLObject;
  }

  /**
   * @deprecated The method is only present for compatibility.
   */
  @Deprecated
  public static Object call(Context cx, Object fun, Object thisArg,
      Object[] args, Scriptable scope) {
    if (!(fun instanceof Function))
      throw notFunctionError(toString(fun));
    Function function = (Function) fun;
    Scriptable thisObj = toObjectOrNull(cx, thisArg);
    if (thisObj == null)
      throw undefCallError(thisObj, "function");
    return function.call(cx, scope, thisObj, args);
  }

  /**
   * Perform function call in reference context. Should always return value that
   * can be passed to {@link #refGet(Ref, Context)} or
   * {@link #refSet(Ref, Object, Context)} arbitrary number of times. The args
   * array reference should not be stored in any object that is can be
   * GC-reachable after this method returns. If this is necessary, store
   * args.clone(), not args array itself.
   */
  public static Ref callRef(Callable function, Scriptable thisObj,
      Object[] args, Context cx) {
    if (function instanceof RefCallable) {
      RefCallable rfunction = (RefCallable) function;
      Ref ref = rfunction.refCall(cx, thisObj, args);
      if (ref == null)
        throw new IllegalStateException(rfunction.getClass().getName()
            + ".refCall() returned null");
      return ref;
    }
    // No runtime support for now
    String msg = getMessage1("msg.no.ref.from.function", toString(function));
    throw constructError("ReferenceError", msg);
  }

  public static Object callSpecial(Context cx, Callable fun,
      Scriptable thisObj, Object[] args, Scriptable scope,
      Scriptable callerThis, int callType, String filename, int lineNumber) {
    if (callType == Node.SPECIALCALL_EVAL) {
      if (NativeGlobal.isEvalFunction(fun))
        return evalSpecial(cx, scope, callerThis, args, filename, lineNumber);
    } else if (callType == Node.SPECIALCALL_WITH) {
      if (NativeWith.isWithFunction(fun))
        throw Context.reportRuntimeError1("msg.only.from.new", "With");
    } else
      throw Kit.codeBug();

    return fun.call(cx, scope, thisObj, args);
  }

  static void checkDeprecated(Context cx, String name) {
    int version = cx.getLanguageVersion();
    if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
      String msg = getMessage1("msg.deprec.ctor", name);
      if (version == Context.VERSION_DEFAULT)
        Context.reportWarning(msg);
      else
        throw Context.reportRuntimeError(msg);
    }
  }

  /**
   * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt> is present
   * on its prototype chain and return <tt>staticTopScope</tt> otherwise. Should
   * only be called when <tt>staticTopScope</tt> is top scope.
   */
  static Scriptable checkDynamicScope(Scriptable possibleDynamicScope,
      Scriptable staticTopScope) {
    // Return cx.topCallScope if scope
    if (possibleDynamicScope == staticTopScope)
      return possibleDynamicScope;
    Scriptable proto = possibleDynamicScope;
    for (;;) {
      proto = proto.getPrototype();
      if (proto == staticTopScope)
        return possibleDynamicScope;
      if (proto == null)
        return staticTopScope;
    }
  }

  public static RegExpProxy checkRegExpProxy(Context cx) {
    RegExpProxy result = getRegExpProxy(cx);
    if (result == null)
      throw Context.reportRuntimeError0("msg.no.regexp");
    return result;
  }

  public static boolean cmp_LE(Object val1, Object val2) {
    double d1, d2;
    if (val1 instanceof Number && val2 instanceof Number) {
      d1 = ((Number) val1).doubleValue();
      d2 = ((Number) val2).doubleValue();
    } else {
      if (val1 instanceof Scriptable)
        val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
      if (val2 instanceof Scriptable)
        val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
      if (val1 instanceof String && val2 instanceof String)
        return ((String) val1).compareTo((String) val2) <= 0;
      d1 = toNumber(val1);
      d2 = toNumber(val2);
    }
    return d1 <= d2;
  }

  public static boolean cmp_LT(Object val1, Object val2) {
    double d1, d2;
    if (val1 instanceof Number && val2 instanceof Number) {
      d1 = ((Number) val1).doubleValue();
      d2 = ((Number) val2).doubleValue();
    } else {
      if (val1 instanceof Scriptable)
        val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
      if (val2 instanceof Scriptable)
        val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
      if (val1 instanceof String && val2 instanceof String)
        return ((String) val1).compareTo((String) val2) < 0;
      d1 = toNumber(val1);
      d2 = toNumber(val2);
    }
    return d1 < d2;
  }

  public static EcmaError constructError(String error, String message) {
    int[] linep = new int[1];
    String filename = Context.getSourcePositionFromStack(linep);
    return constructError(error, message, filename, linep[0], null, 0);
  }

  public static EcmaError constructError(String error, String message,
      int lineNumberDelta) {
    int[] linep = new int[1];
    String filename = Context.getSourcePositionFromStack(linep);
    if (linep[0] != 0)
      linep[0] += lineNumberDelta;
    return constructError(error, message, filename, linep[0], null, 0);
  }

  public static EcmaError constructError(String error, String message,
      String sourceName, int lineNumber, String lineSource, int columnNumber) {
    return new EcmaError(error, message, sourceName, lineNumber, lineSource,
        columnNumber);
  }

  public static Scriptable createFunctionActivation(NativeFunction funObj,
      Scriptable scope, Object[] args) {
    return new NativeCall(funObj, scope, args);
  }

  private static XMLLib currentXMLLib(Context cx) {
    // Scripts should be running to access this
    if (cx.topCallScope == null)
      throw new IllegalStateException();

    XMLLib xmlLib = cx.cachedXMLLib;
    if (xmlLib == null) {
      xmlLib = XMLLib.extractFromScope(cx.topCallScope);
      if (xmlLib == null)
        throw new IllegalStateException();
      cx.cachedXMLLib = xmlLib;
    }

    return xmlLib;
  }

  static String defaultObjectToSource(Context cx, Scriptable scope,
      Scriptable thisObj, Object[] args) {
    boolean toplevel, iterating;
    if (cx.iterating == null) {
      toplevel = true;
      iterating = false;
      cx.iterating = new ObjToIntMap(31);
    } else {
      toplevel = false;
      iterating = cx.iterating.has(thisObj);
    }

    StringBuffer result = new StringBuffer(128);
    if (toplevel)
      result.append("(");
    result.append('{');

    // Make sure cx.iterating is set to null when done
    // so we don't leak memory
    try {
      if (!iterating) {
        cx.iterating.intern(thisObj); // stop recursion.
        Object[] ids = thisObj.getIds();
        for (int i = 0; i < ids.length; i++) {
          Object id = ids[i];
          Object value;
          if (id instanceof Integer) {
            int intId = ((Integer) id).intValue();
            value = thisObj.get(intId, thisObj);
            if (value == Scriptable.NOT_FOUND)
              continue; // a property has been removed
            if (i > 0)
              result.append(", ");
            result.append(intId);
          } else {
            String strId = (String) id;
            value = thisObj.get(strId, thisObj);
            if (value == Scriptable.NOT_FOUND)
              continue; // a property has been removed
            if (i > 0)
              result.append(", ");
            if (ScriptRuntime.isValidIdentifierName(strId))
              result.append(strId);
            else {
              result.append('\'');
              result.append(ScriptRuntime.escapeString(strId, '\''));
              result.append('\'');
            }
          }
          result.append(':');
          result.append(ScriptRuntime.uneval(cx, scope, value));
        }
      }
    } finally {
      if (toplevel)
        cx.iterating = null;
    }

    result.append('}');
    if (toplevel)
      result.append(')');
    return result.toString();
  }

  static String defaultObjectToString(Scriptable obj) {
    return "[object " + obj.getClassName() + ']';
  }

  /**
   * The delete operator
   * 
   * See ECMA 11.4.1
   * 
   * In ECMA 0.19, the description of the delete operator (11.4.1) assumes that
   * the [[Delete]] method returns a value. However, the definition of the
   * [[Delete]] operator (8.6.2.5) does not define a return value. Here we
   * assume that the [[Delete]] method doesn't return a value.
   */
  public static Object delete(Object obj, Object id, Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null) {
      String idStr = id == null ? "null" : id.toString();
      throw typeError2("msg.undef.prop.delete", toString(obj), idStr);
    }
    boolean result = deleteObjectElem(sobj, id, cx);
    return wrapBoolean(result);
  }

  public static boolean deleteObjectElem(Scriptable target, Object elem,
      Context cx) {
    boolean result;
    if (target instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) target;
      result = xmlObject.ecmaDelete(cx, elem);
    } else {
      String s = toStringIdOrIndex(cx, elem);
      if (s == null) {
        int index = lastIndexResult(cx);
        result = ScriptableObject.deleteProperty(target, index);
      } else
        result = ScriptableObject.deleteProperty(target, s);
    }
    return result;
  }

  private static Object doScriptableIncrDecr(Scriptable target, String id,
      Scriptable protoChainStart, Object value, int incrDecrMask) {
    boolean post = (incrDecrMask & Node.POST_FLAG) != 0;
    double number;
    if (value instanceof Number)
      number = ((Number) value).doubleValue();
    else {
      number = toNumber(value);
      if (post)
        // convert result to number
        value = wrapNumber(number);
    }
    if ((incrDecrMask & Node.DECR_FLAG) == 0)
      ++number;
    else
      --number;
    Number result = wrapNumber(number);
    target.put(id, protoChainStart, result);
    if (post)
      return value;
    else
      return result;
  }

  public static Object doTopCall(Callable callable, Context cx,
      Scriptable scope, Scriptable thisObj, Object[] args) {
    if (scope == null)
      throw new IllegalArgumentException();
    if (cx.topCallScope != null)
      throw new IllegalStateException();

    Object result;
    cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
    cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE);
    ContextFactory f = cx.getFactory();
    try {
      result = f.doTopCall(callable, cx, scope, thisObj, args);
    } finally {
      cx.topCallScope = null;
      // Cleanup cached references
      cx.cachedXMLLib = null;

      if (cx.currentActivationCall != null)
        // Function should always call exitActivationFunction
        // if it creates activation record
        throw new IllegalStateException();
    }
    return result;
  }

  public static Object elemIncrDecr(Object obj, Object index, Context cx,
      int incrDecrMask) {
    Object value = getObjectElem(obj, index, cx);
    boolean post = (incrDecrMask & Node.POST_FLAG) != 0;
    double number;
    if (value instanceof Number)
      number = ((Number) value).doubleValue();
    else {
      number = toNumber(value);
      if (post)
        // convert result to number
        value = wrapNumber(number);
    }
    if ((incrDecrMask & Node.DECR_FLAG) == 0)
      ++number;
    else
      --number;
    Number result = wrapNumber(number);
    setObjectElem(obj, index, result, cx);
    if (post)
      return value;
    else
      return result;
  }

  public static void enterActivationFunction(Context cx, Scriptable scope) {
    if (cx.topCallScope == null)
      throw new IllegalStateException();
    NativeCall call = (NativeCall) scope;
    call.parentActivationCall = cx.currentActivationCall;
    cx.currentActivationCall = call;
  }

  public static Scriptable enterDotQuery(Object value, Scriptable scope) {
    if (!(value instanceof XMLObject))
      throw notXmlError(value);
    XMLObject object = (XMLObject) value;
    return object.enterDotQuery(scope);
  }

  public static Scriptable enterWith(Object obj, Context cx, Scriptable scope) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw typeError1("msg.undef.with", toString(obj));
    if (sobj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) sobj;
      return xmlObject.enterWith(scope);
    }
    return new NativeWith(scope, sobj);
  }

  private static void enumChangeObject(IdEnumeration x) {
    Object[] ids = null;
    while (x.obj != null) {
      ids = x.obj.getIds();
      if (ids.length != 0)
        break;
      x.obj = x.obj.getPrototype();
    }
    if (x.obj != null && x.ids != null) {
      Object[] previous = x.ids;
      int L = previous.length;
      if (x.used == null)
        x.used = new ObjToIntMap(L);
      for (int i = 0; i != L; ++i)
        x.used.intern(previous[i]);
    }
    x.ids = ids;
    x.index = 0;
  }

  public static Object enumId(Object enumObj, Context cx) {
    IdEnumeration x = (IdEnumeration) enumObj;
    if (x.iterator != null)
      return x.currentId;
    switch (x.enumType) {
      case ENUMERATE_KEYS :
      case ENUMERATE_KEYS_NO_ITERATOR :
        return x.currentId;
      case ENUMERATE_VALUES :
      case ENUMERATE_VALUES_NO_ITERATOR :
        return enumValue(enumObj, cx);
      case ENUMERATE_ARRAY :
      case ENUMERATE_ARRAY_NO_ITERATOR :
        Object[] elements = {x.currentId, enumValue(enumObj, cx)};
        return cx.newArray(x.obj, elements);
      default :
        throw Kit.codeBug();
    }
  }

  // for backwards compatibility with generated class files
  public static Object enumInit(Object value, Context cx, boolean enumValues) {
    return enumInit(value, cx, enumValues ? ENUMERATE_VALUES : ENUMERATE_KEYS);
  }

  public static Object enumInit(Object value, Context cx, int enumType) {
    IdEnumeration x = new IdEnumeration();
    x.obj = toObjectOrNull(cx, value);
    if (x.obj == null)
      // null or undefined do not cause errors but rather lead to empty
      // "for in" loop
      return x;
    x.enumType = enumType;
    x.iterator = null;
    if (enumType != ENUMERATE_KEYS_NO_ITERATOR
        && enumType != ENUMERATE_VALUES_NO_ITERATOR
        && enumType != ENUMERATE_ARRAY_NO_ITERATOR)
      x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, true);
    if (x.iterator == null)
      // enumInit should read all initial ids before returning
      // or "for (a.i in a)" would wrongly enumerate i in a as well
      enumChangeObject(x);

    return x;
  }

  public static Boolean enumNext(Object enumObj) {
    IdEnumeration x = (IdEnumeration) enumObj;
    if (x.iterator != null) {
      Object v = ScriptableObject.getProperty(x.iterator, "next");
      if (!(v instanceof Callable))
        return Boolean.FALSE;
      Callable f = (Callable) v;
      Context cx = Context.getContext();
      try {
        x.currentId = f.call(cx, x.iterator.getParentScope(), x.iterator,
            emptyArgs);
        return Boolean.TRUE;
      } catch (JavaScriptException e) {
        if (e.getValue() instanceof NativeIterator.StopIteration)
          return Boolean.FALSE;
        throw e;
      }
    }
    for (;;) {
      if (x.obj == null)
        return Boolean.FALSE;
      if (x.index == x.ids.length) {
        x.obj = x.obj.getPrototype();
        enumChangeObject(x);
        continue;
      }
      Object id = x.ids[x.index++];
      if (x.used != null && x.used.has(id))
        continue;
      if (id instanceof String) {
        String strId = (String) id;
        if (!x.obj.has(strId, x.obj))
          continue; // must have been deleted
        x.currentId = strId;
      } else {
        int intId = ((Number) id).intValue();
        if (!x.obj.has(intId, x.obj))
          continue; // must have been deleted
        x.currentId = x.enumNumbers ? (Object) new Integer(intId) : String
            .valueOf(intId);
      }
      return Boolean.TRUE;
    }
  }

  public static Object enumValue(Object enumObj, Context cx) {
    IdEnumeration x = (IdEnumeration) enumObj;

    Object result;

    String s = toStringIdOrIndex(cx, x.currentId);
    if (s == null) {
      int index = lastIndexResult(cx);
      result = x.obj.get(index, x.obj);
    } else
      result = x.obj.get(s, x.obj);

    return result;
  }

  /**
   * Equality
   * 
   * See ECMA 11.9
   */
  public static boolean eq(Object x, Object y) {
    if (x == null || x == Undefined.instance) {
      if (y == null || y == Undefined.instance)
        return true;
      if (y instanceof ScriptableObject) {
        Object test = ((ScriptableObject) y).equivalentValues(x);
        if (test != Scriptable.NOT_FOUND)
          return ((Boolean) test).booleanValue();
      }
      return false;
    } else if (x instanceof Number)
      return eqNumber(((Number) x).doubleValue(), y);
    else if (x instanceof String)
      return eqString((String) x, y);
    else if (x instanceof Boolean) {
      boolean b = ((Boolean) x).booleanValue();
      if (y instanceof Boolean)
        return b == ((Boolean) y).booleanValue();
      if (y instanceof ScriptableObject) {
        Object test = ((ScriptableObject) y).equivalentValues(x);
        if (test != Scriptable.NOT_FOUND)
          return ((Boolean) test).booleanValue();
      }
      return eqNumber(b ? 1.0 : 0.0, y);
    } else if (x instanceof Scriptable) {
      if (y instanceof Scriptable) {
        if (x == y)
          return true;
        if (x instanceof ScriptableObject) {
          Object test = ((ScriptableObject) x).equivalentValues(y);
          if (test != Scriptable.NOT_FOUND)
            return ((Boolean) test).booleanValue();
        }
        if (y instanceof ScriptableObject) {
          Object test = ((ScriptableObject) y).equivalentValues(x);
          if (test != Scriptable.NOT_FOUND)
            return ((Boolean) test).booleanValue();
        }
        if (x instanceof Wrapper && y instanceof Wrapper) {
          // See bug 413838. Effectively an extension to ECMA for
          // the LiveConnect case.
          Object unwrappedX = ((Wrapper) x).unwrap();
          Object unwrappedY = ((Wrapper) y).unwrap();
          return unwrappedX == unwrappedY || isPrimitive(unwrappedX)
              && isPrimitive(unwrappedY) && eq(unwrappedX, unwrappedY);
        }
        return false;
      } else if (y instanceof Boolean) {
        if (x instanceof ScriptableObject) {
          Object test = ((ScriptableObject) x).equivalentValues(y);
          if (test != Scriptable.NOT_FOUND)
            return ((Boolean) test).booleanValue();
        }
        double d = ((Boolean) y).booleanValue() ? 1.0 : 0.0;
        return eqNumber(d, x);
      } else if (y instanceof Number)
        return eqNumber(((Number) y).doubleValue(), x);
      else if (y instanceof String)
        return eqString((String) y, x);
      // covers the case when y == Undefined.instance as well
      return false;
    } else {
      warnAboutNonJSObject(x);
      return x == y;
    }
  }

  static boolean eqNumber(double x, Object y) {
    for (;;)
      if (y == null || y == Undefined.instance)
        return false;
      else if (y instanceof Number)
        return x == ((Number) y).doubleValue();
      else if (y instanceof String)
        return x == toNumber(y);
      else if (y instanceof Boolean)
        return x == (((Boolean) y).booleanValue() ? 1.0 : +0.0);
      else if (y instanceof Scriptable) {
        if (y instanceof ScriptableObject) {
          Object xval = wrapNumber(x);
          Object test = ((ScriptableObject) y).equivalentValues(xval);
          if (test != Scriptable.NOT_FOUND)
            return ((Boolean) test).booleanValue();
        }
        y = toPrimitive(y);
      } else {
        warnAboutNonJSObject(y);
        return false;
      }
  }

  private static boolean eqString(String x, Object y) {
    for (;;)
      if (y == null || y == Undefined.instance)
        return false;
      else if (y instanceof String)
        return x.equals(y);
      else if (y instanceof Number)
        return toNumber(x) == ((Number) y).doubleValue();
      else if (y instanceof Boolean)
        return toNumber(x) == (((Boolean) y).booleanValue() ? 1.0 : 0.0);
      else if (y instanceof Scriptable) {
        if (y instanceof ScriptableObject) {
          Object test = ((ScriptableObject) y).equivalentValues(x);
          if (test != Scriptable.NOT_FOUND)
            return ((Boolean) test).booleanValue();
        }
        y = toPrimitive(y);
        continue;
      } else {
        warnAboutNonJSObject(y);
        return false;
      }
  }

  private static RuntimeException errorWithClassName(String msg, Object val) {
    return Context.reportRuntimeError1(msg, val.getClass().getName());
  }

  /**
   * Escapes the reserved characters in a value of an attribute
   * 
   * @param value
   *          Unescaped text
   * @return The escaped text
   */
  public static String escapeAttributeValue(Object value, Context cx) {
    XMLLib xmlLib = currentXMLLib(cx);
    return xmlLib.escapeAttributeValue(value);
  }

  public static String escapeString(String s) {
    return escapeString(s, '"');
  }

  /**
   * For escaping strings printed by object and array literals; not quite the
   * same as 'escape.'
   */
  public static String escapeString(String s, char escapeQuote) {
    if (!(escapeQuote == '"' || escapeQuote == '\''))
      Kit.codeBug();
    StringBuffer sb = null;

    for (int i = 0, L = s.length(); i != L; ++i) {
      int c = s.charAt(i);

      if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
        // an ordinary print character (like C isprint()) and not "
        // or \ .
        if (sb != null)
          sb.append((char) c);
        continue;
      }
      if (sb == null) {
        sb = new StringBuffer(L + 3);
        sb.append(s);
        sb.setLength(i);
      }

      int escape = -1;
      switch (c) {
        case '\b' :
          escape = 'b';
          break;
        case '\f' :
          escape = 'f';
          break;
        case '\n' :
          escape = 'n';
          break;
        case '\r' :
          escape = 'r';
          break;
        case '\t' :
          escape = 't';
          break;
        case 0xb :
          escape = 'v';
          break; // Java lacks \v.
        case ' ' :
          escape = ' ';
          break;
        case '\\' :
          escape = '\\';
          break;
      }
      if (escape >= 0) {
        // an \escaped sort of character
        sb.append('\\');
        sb.append((char) escape);
      } else if (c == escapeQuote) {
        sb.append('\\');
        sb.append(escapeQuote);
      } else {
        int hexSize;
        if (c < 256) {
          // 2-digit hex
          sb.append("\\x");
          hexSize = 2;
        } else {
          // Unicode.
          sb.append("\\u");
          hexSize = 4;
        }
        // append hexadecimal form of c left-padded with 0
        for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
          int digit = 0xf & c >> shift;
          int hc = digit < 10 ? '0' + digit : 'a' - 10 + digit;
          sb.append((char) hc);
        }
      }
    }
    return sb == null ? s : sb.toString();
  }

  /**
   * Escapes the reserved characters in a value of a text node
   * 
   * @param value
   *          Unescaped text
   * @return The escaped text
   */
  public static String escapeTextValue(Object value, Context cx) {
    XMLLib xmlLib = currentXMLLib(cx);
    return xmlLib.escapeTextValue(value);
  }

  /**
   * The eval function property of the global object.
   * 
   * See ECMA 15.1.2.1
   */
  public static Object evalSpecial(Context cx, Scriptable scope,
      Object thisArg, Object[] args, String filename, int lineNumber) {
    if (args.length < 1)
      return Undefined.instance;
    Object x = args[0];
    if (!(x instanceof String)) {
      if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
          || cx.hasFeature(Context.FEATURE_STRICT_EVAL))
        throw Context.reportRuntimeError0("msg.eval.nonstring.strict");
      String message = ScriptRuntime.getMessage0("msg.eval.nonstring");
      Context.reportWarning(message);
      return x;
    }
    if (filename == null) {
      int[] linep = new int[1];
      filename = Context.getSourcePositionFromStack(linep);
      if (filename != null)
        lineNumber = linep[0];
      else
        filename = "";
    }
    String sourceName = ScriptRuntime.makeUrlForGeneratedScript(true, filename,
        lineNumber);

    ErrorReporter reporter;
    reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());

    Evaluator evaluator = Context.createInterpreter();
    if (evaluator == null)
      throw new JavaScriptException("Interpreter not present", filename,
          lineNumber);

    // Compile with explicit interpreter instance to force interpreter
    // mode.
    Script script = cx.compileString((String) x, evaluator, reporter,
        sourceName, 1, null);
    evaluator.setEvalScriptFlag(script);
    Callable c = (Callable) script;
    return c.call(cx, scope, (Scriptable) thisArg, ScriptRuntime.emptyArgs);
  }

  public static void exitActivationFunction(Context cx) {
    NativeCall call = cx.currentActivationCall;
    cx.currentActivationCall = call.parentActivationCall;
    call.parentActivationCall = null;
  }

  static NativeCall findFunctionActivation(Context cx, Function f) {
    NativeCall call = cx.currentActivationCall;
    while (call != null) {
      if (call.function == f)
        return call;
      call = call.parentActivationCall;
    }
    return null;
  }

  static Object[] getApplyArguments(Context cx, Object arg1) {
    if (arg1 == null || arg1 == Undefined.instance)
      return ScriptRuntime.emptyArgs;
    else if (arg1 instanceof NativeArray || arg1 instanceof Arguments)
      return cx.getElements((Scriptable) arg1);
    else
      throw ScriptRuntime.typeError0("msg.arg.isnt.array");
  }

  public static Object[] getArrayElements(Scriptable object) {
    Context cx = Context.getContext();
    long longLen = NativeArray.getLengthProperty(cx, object);
    if (longLen > Integer.MAX_VALUE)
      // arrays beyond MAX_INT is not in Java in any case
      throw new IllegalArgumentException();
    int len = (int) longLen;
    if (len == 0)
      return ScriptRuntime.emptyArgs;
    else {
      Object[] result = new Object[len];
      for (int i = 0; i < len; i++) {
        Object elem = ScriptableObject.getProperty(object, i);
        result[i] = elem == Scriptable.NOT_FOUND ? Undefined.instance : elem;
      }
      return result;
    }
  }

  static Callable getCallable(Scriptable thisObj) {
    Callable function;
    if (thisObj instanceof Callable)
      function = (Callable) thisObj;
    else {
      Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
      if (!(value instanceof Callable))
        throw ScriptRuntime.notFunctionError(value, thisObj);
      function = (Callable) value;
    }
    return function;
  }

  /**
   * Prepare for calling obj[id](...): return function corresponding to obj[id]
   * and make obj properly converted to Scriptable available as
   * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller
   * must call ScriptRuntime.lastStoredScriptable() immediately after calling
   * this method.
   */
  public static Callable getElemFunctionAndThis(Object obj, Object elem,
      Context cx) {
    String s = toStringIdOrIndex(cx, elem);
    if (s != null)
      return getPropFunctionAndThis(obj, s, cx);
    int index = lastIndexResult(cx);

    Scriptable thisObj = toObjectOrNull(cx, obj);
    if (thisObj == null)
      throw undefCallError(obj, String.valueOf(index));

    Object value;
    for (;;) {
      // Ignore XML lookup as required by ECMA 357, 11.2.2.1
      value = ScriptableObject.getProperty(thisObj, index);
      if (value != Scriptable.NOT_FOUND)
        break;
      if (!(thisObj instanceof XMLObject))
        break;
      XMLObject xmlObject = (XMLObject) thisObj;
      Scriptable extra = xmlObject.getExtraMethodSource(cx);
      if (extra == null)
        break;
      thisObj = extra;
    }
    if (!(value instanceof Callable))
      throw notFunctionError(value, elem);

    storeScriptable(cx, thisObj);
    return (Callable) value;
  }

  static Function getExistingCtor(Context cx, Scriptable scope,
      String constructorName) {
    Object ctorVal = ScriptableObject.getProperty(scope, constructorName);
    if (ctorVal instanceof Function)
      return (Function) ctorVal;
    if (ctorVal == Scriptable.NOT_FOUND)
      throw Context.reportRuntimeError1("msg.ctor.not.found", constructorName);
    else
      throw Context.reportRuntimeError1("msg.not.ctor", constructorName);
  }

  public static ScriptableObject getGlobal(Context cx) {
    final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
    Class<?> globalClass = Kit.classOrNull(GLOBAL_CLASS);
    if (globalClass != null)
      try {
        Class<?>[] parm = {ScriptRuntime.ContextClass};
        Constructor<?> globalClassCtor = globalClass.getConstructor(parm);
        Object[] arg = {cx};
        return (ScriptableObject) globalClassCtor.newInstance(arg);
      } catch (Exception e) {
        // fall through...
      }
    return new ImporterTopLevel(cx);
  }

  /**
   * If d is exact int value, return its value wrapped as Integer and othewise
   * return d converted to String.
   */
  static Object getIndexObject(double d) {
    int i = (int) d;
    if (i == d)
      return new Integer(i);
    return toString(d);
  }

  /**
   * If s represents index, then return index value wrapped as Integer and
   * othewise return s.
   */
  static Object getIndexObject(String s) {
    long indexTest = indexFromString(s);
    if (indexTest >= 0)
      return new Integer((int) indexTest);
    return s;
  }

  public static ScriptableObject getLibraryScopeOrNull(Scriptable scope) {
    ScriptableObject libScope;
    libScope = (ScriptableObject) ScriptableObject.getTopScopeValue(scope,
        LIBRARY_SCOPE_KEY);
    return libScope;
  }

  public static String getMessage(String messageId, Object[] arguments) {
    return messageProvider.getMessage(messageId, arguments);
  }

  public static String getMessage0(String messageId) {
    return getMessage(messageId, null);
  }

  public static String getMessage1(String messageId, Object arg1) {
    Object[] arguments = {arg1};
    return getMessage(messageId, arguments);
  }

  public static String getMessage2(String messageId, Object arg1, Object arg2) {
    Object[] arguments = {arg1, arg2};
    return getMessage(messageId, arguments);
  }

  public static String getMessage3(String messageId, Object arg1, Object arg2,
      Object arg3) {
    Object[] arguments = {arg1, arg2, arg3};
    return getMessage(messageId, arguments);
  }

  public static String getMessage4(String messageId, Object arg1, Object arg2,
      Object arg3, Object arg4) {
    Object[] arguments = {arg1, arg2, arg3, arg4};
    return getMessage(messageId, arguments);
  }

  /**
   * Prepare for calling name(...): return function corresponding to name and
   * make current top scope available as ScriptRuntime.lastStoredScriptable()
   * for consumption as thisObj. The caller must call
   * ScriptRuntime.lastStoredScriptable() immediately after calling this method.
   */
  public static Callable getNameFunctionAndThis(String name, Context cx,
      Scriptable scope) {
    Scriptable parent = scope.getParentScope();
    if (parent == null) {
      Object result = topScopeName(cx, scope, name);
      if (!(result instanceof Callable))
        if (result == Scriptable.NOT_FOUND)
          throw notFoundError(scope, name);
        else
          throw notFunctionError(result, name);
      // Top scope is not NativeWith or NativeCall => thisObj == scope
      Scriptable thisObj = scope;
      storeScriptable(cx, thisObj);
      return (Callable) result;
    }

    // name will call storeScriptable(cx, thisObj);
    return (Callable) nameOrFunction(cx, scope, parent, name, true);
  }

  /**
   * Call obj.[[Get]](id)
   */
  public static Object getObjectElem(Object obj, Object elem, Context cx) {
    return getObjectElem(obj, elem, cx, getTopCallScope(cx));
  }

  /**
   * Call obj.[[Get]](id)
   */
  public static Object getObjectElem(Object obj, Object elem, Context cx,
      final Scriptable scope) {
    Scriptable sobj = toObjectOrNull(cx, obj, scope);
    if (sobj == null)
      throw undefReadError(obj, elem);
    return getObjectElem(sobj, elem, cx);
  }

  public static Object getObjectElem(Scriptable obj, Object elem, Context cx) {
    if (obj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) obj;
      return xmlObject.ecmaGet(cx, elem);
    }

    Object result;

    String s = toStringIdOrIndex(cx, elem);
    if (s == null) {
      int index = lastIndexResult(cx);
      result = ScriptableObject.getProperty(obj, index);
    } else
      result = ScriptableObject.getProperty(obj, s);

    if (result == Scriptable.NOT_FOUND)
      result = Undefined.instance;

    return result;
  }

  /*
   * A cheaper and less general version of the above for well-known argument
   * types.
   */
  public static Object getObjectIndex(Object obj, double dblIndex, Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefReadError(obj, toString(dblIndex));

    int index = (int) dblIndex;
    if (index == dblIndex)
      return getObjectIndex(sobj, index, cx);
    else {
      String s = toString(dblIndex);
      return getObjectProp(sobj, s, cx);
    }
  }

  public static Object getObjectIndex(Scriptable obj, int index, Context cx) {
    if (obj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) obj;
      return xmlObject.ecmaGet(cx, new Integer(index));
    }

    Object result = ScriptableObject.getProperty(obj, index);
    if (result == Scriptable.NOT_FOUND)
      result = Undefined.instance;

    return result;
  }

  /**
   * Version of getObjectElem when elem is a valid JS identifier name.
   */
  public static Object getObjectProp(Object obj, String property, Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefReadError(obj, property);
    return getObjectProp(sobj, property, cx);
  }

  /**
   * @param scope
   *          the scope that should be used to resolve primitive prototype
   */
  public static Object getObjectProp(Object obj, String property, Context cx,
      final Scriptable scope) {
    Scriptable sobj = toObjectOrNull(cx, obj, scope);
    if (sobj == null)
      throw undefReadError(obj, property);
    return getObjectProp(sobj, property, cx);
  }

  public static Object getObjectProp(Scriptable obj, String property, Context cx) {
    if (obj instanceof XMLObject) {
      // TODO: Change XMLObject to just use Scriptable interface
      // to avoid paying cost of instanceof check on *every property
      // lookup* !
      XMLObject xmlObject = (XMLObject) obj;
      return xmlObject.ecmaGet(cx, property);
    }

    Object result = ScriptableObject.getProperty(obj, property);
    if (result == Scriptable.NOT_FOUND) {
      if (cx.hasFeature(Context.FEATURE_STRICT_MODE))
        Context.reportWarning(ScriptRuntime.getMessage1(
            "msg.ref.undefined.prop", property));
      result = Undefined.instance;
    }

    return result;
  }

  public static Object getObjectPropNoWarn(Object obj, String property,
      Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefReadError(obj, property);
    if (obj instanceof XMLObject)
      // TODO: fix as mentioned in note in method above
      getObjectProp(sobj, property, cx);
    Object result = ScriptableObject.getProperty(sobj, property);
    if (result == Scriptable.NOT_FOUND)
      return Undefined.instance;
    return result;
  }
  /**
   * Prepare for calling obj.property(...): return function corresponding to
   * obj.property and make obj properly converted to Scriptable available as
   * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller
   * must call ScriptRuntime.lastStoredScriptable() immediately after calling
   * this method. Warning: this doesn't allow to resolve primitive prototype
   * properly when many top scopes are involved.
   */
  public static Callable getPropFunctionAndThis(Object obj, String property,
      Context cx) {
    Scriptable thisObj = toObjectOrNull(cx, obj);
    return getPropFunctionAndThisHelper(obj, property, cx, thisObj);
  }
  /**
   * Prepare for calling obj.property(...): return function corresponding to
   * obj.property and make obj properly converted to Scriptable available as
   * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller
   * must call ScriptRuntime.lastStoredScriptable() immediately after calling
   * this method.
   */
  public static Callable getPropFunctionAndThis(Object obj, String property,
      Context cx, final Scriptable scope) {
    Scriptable thisObj = toObjectOrNull(cx, obj, scope);
    return getPropFunctionAndThisHelper(obj, property, cx, thisObj);
  }
  private static Callable getPropFunctionAndThisHelper(Object obj,
      String property, Context cx, Scriptable thisObj) {
    if (thisObj == null)
      throw undefCallError(obj, property);

    Object value;
    for (;;) {
      // Ignore XML lookup as required by ECMA 357, 11.2.2.1
      value = ScriptableObject.getProperty(thisObj, property);
      if (value != Scriptable.NOT_FOUND)
        break;
      if (!(thisObj instanceof XMLObject))
        break;
      XMLObject xmlObject = (XMLObject) thisObj;
      Scriptable extra = xmlObject.getExtraMethodSource(cx);
      if (extra == null)
        break;
      thisObj = extra;
    }

    if (!(value instanceof Callable)) {
      Object noSuchMethod = ScriptableObject.getProperty(thisObj,
          "__noSuchMethod__");
      if (noSuchMethod instanceof Callable)
        value = new NoSuchMethodShim((Callable) noSuchMethod, property);
      else
        throw notFunctionError(thisObj, value, property);
    }

    storeScriptable(cx, thisObj);
    return (Callable) value;
  }
  public static RegExpProxy getRegExpProxy(Context cx) {
    return cx.getRegExpProxy();
  }
  public static Scriptable getTopCallScope(Context cx) {
    Scriptable scope = cx.topCallScope;
    if (scope == null)
      throw new IllegalStateException();
    return scope;
  }

  public static Object getTopLevelProp(Scriptable scope, String id) {
    scope = ScriptableObject.getTopLevelScope(scope);
    return ScriptableObject.getProperty(scope, id);
  }

  /**
   * Prepare for calling <expression>(...): return function corresponding to
   * <expression> and make parent scope of the function available as
   * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller
   * must call ScriptRuntime.lastStoredScriptable() immediately after calling
   * this method.
   */
  public static Callable getValueFunctionAndThis(Object value, Context cx) {
    if (!(value instanceof Callable))
      throw notFunctionError(value);

    Callable f = (Callable) value;
    Scriptable thisObj = null;
    if (f instanceof Scriptable)
      thisObj = ((Scriptable) f).getParentScope();
    if (thisObj == null) {
      if (cx.topCallScope == null)
        throw new IllegalStateException();
      thisObj = cx.topCallScope;
    }
    if (thisObj.getParentScope() != null)
      if (thisObj instanceof NativeWith) {
        // functions defined inside with should have with target
        // as their thisObj
      } else if (thisObj instanceof NativeCall)
        // nested functions should have top scope as their thisObj
        thisObj = ScriptableObject.getTopLevelScope(thisObj);
    storeScriptable(cx, thisObj);
    return f;
  }

  public static boolean hasObjectElem(Scriptable target, Object elem, Context cx) {
    boolean result;

    if (target instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) target;
      result = xmlObject.ecmaHas(cx, elem);
    } else {
      String s = toStringIdOrIndex(cx, elem);
      if (s == null) {
        int index = lastIndexResult(cx);
        result = ScriptableObject.hasProperty(target, index);
      } else
        result = ScriptableObject.hasProperty(target, s);
    }

    return result;
  }

  public static boolean hasTopCall(Context cx) {
    return cx.topCallScope != null;
  }

  /**
   * The in operator.
   * 
   * This is a new JS 1.3 language feature. The in operator mirrors the
   * operation of the for .. in construct, and tests whether the rhs has the
   * property given by the lhs. It is different from the for .. in construct in
   * that: <BR>
   * - it doesn't perform ToObject on the right hand side <BR>
   * - it returns true for DontEnum properties.
   * 
   * @param a
   *          the left hand operand
   * @param b
   *          the right hand operand
   * 
   * @return true if property name or element number a is a property of b
   */
  public static boolean in(Object a, Object b, Context cx) {
    if (!(b instanceof Scriptable))
      throw typeError0("msg.instanceof.not.object");

    return hasObjectElem((Scriptable) b, a, cx);
  }

  /**
   * Return -1L if str is not an index or the index value as lower 32 bits of
   * the result.
   */
  private static long indexFromString(String str) {
    // The length of the decimal string representation of
    // Integer.MAX_VALUE, 2147483647
    final int MAX_VALUE_LENGTH = 10;

    int len = str.length();
    if (len > 0) {
      int i = 0;
      boolean negate = false;
      int c = str.charAt(0);
      if (c == '-')
        if (len > 1) {
          c = str.charAt(1);
          i = 1;
          negate = true;
        }
      c -= '0';
      if (0 <= c && c <= 9
          && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) {
        // Use negative numbers to accumulate index to handle
        // Integer.MIN_VALUE that is greater by 1 in absolute value
        // then Integer.MAX_VALUE
        int index = -c;
        int oldIndex = 0;
        i++;
        if (index != 0)
          // Note that 00, 01, 000 etc. are not indexes
          while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9) {
            oldIndex = index;
            index = 10 * index - c;
            i++;
          }
        // Make sure all characters were consumed and that it couldn't
        // have overflowed.
        if (i == len
            && (oldIndex > Integer.MIN_VALUE / 10 || oldIndex == Integer.MIN_VALUE / 10
                && c <= (negate
                    ? -(Integer.MIN_VALUE % 10)
                    : Integer.MAX_VALUE % 10)))
          return 0xFFFFFFFFL & (negate ? index : -index);
      }
    }
    return -1L;
  }

  public static void initFunction(Context cx, Scriptable scope,
      NativeFunction function, int type, boolean fromEvalCode) {
    if (type == FunctionNode.FUNCTION_STATEMENT) {
      String name = function.getFunctionName();
      if (name != null && name.length() != 0)
        if (!fromEvalCode)
          // ECMA specifies that functions defined in global and
          // function scope outside eval should have DONTDELETE set.
          ScriptableObject.defineProperty(scope, name, function,
              ScriptableObject.PERMANENT);
        else
          scope.put(name, scope, function);
    } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
      String name = function.getFunctionName();
      if (name != null && name.length() != 0) {
        // Always put function expression statements into initial
        // activation object ignoring the with statement to follow
        // SpiderMonkey
        while (scope instanceof NativeWith)
          scope = scope.getParentScope();
        scope.put(name, scope, function);
      }
    } else
      throw Kit.codeBug();
  }

  public static void initScript(NativeFunction funObj, Scriptable thisObj,
      Context cx, Scriptable scope, boolean evalScript) {
    if (cx.topCallScope == null)
      throw new IllegalStateException();

    int varCount = funObj.getParamAndVarCount();
    if (varCount != 0) {

      Scriptable varScope = scope;
      // Never define any variables from var statements inside with
      // object. See bug 38590.
      while (varScope instanceof NativeWith)
        varScope = varScope.getParentScope();

      for (int i = varCount; i-- != 0;) {
        String name = funObj.getParamOrVarName(i);
        boolean isConst = funObj.getParamOrVarConst(i);
        // Don't overwrite existing def if already defined in object
        // or prototypes of object.
        if (!ScriptableObject.hasProperty(scope, name)) {
          if (!evalScript) {
            // Global var definitions are supposed to be DONTDELETE
            if (isConst)
              ScriptableObject.defineConstProperty(varScope, name);
            else
              ScriptableObject.defineProperty(varScope, name,
                  Undefined.instance, ScriptableObject.PERMANENT);
          } else
            varScope.put(name, varScope, Undefined.instance);
        } else
          ScriptableObject.redefineProperty(scope, name, isConst);
      }
    }
  }

  public static ScriptableObject initStandardObjects(Context cx,
      ScriptableObject scope, boolean sealed) {
    if (scope == null)
      scope = new NativeObject();
    scope.associateValue(LIBRARY_SCOPE_KEY, scope);
    new ClassCache().associate(scope);

    BaseFunction.init(scope, sealed);
    NativeObject.init(scope, sealed);

    Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);

    // Function.prototype.__proto__ should be Object.prototype
    Scriptable functionProto = ScriptableObject.getFunctionPrototype(scope);
    functionProto.setPrototype(objectProto);

    // Set the prototype of the object passed in if need be
    if (scope.getPrototype() == null)
      scope.setPrototype(objectProto);

    // must precede NativeGlobal since it's needed therein
    NativeError.init(scope, sealed);
    NativeGlobal.init(cx, scope, sealed);

    NativeArray.init(scope, sealed);
    if (cx.getOptimizationLevel() > 0)
      // When optimizing, attempt to fulfill all requests for new Array(N)
      // with a higher threshold before switching to a sparse
      // representation
      NativeArray.setMaximumInitialCapacity(200000);
    NativeString.init(scope, sealed);
    NativeBoolean.init(scope, sealed);
    NativeNumber.init(scope, sealed);
    NativeDate.init(scope, sealed);
    NativeMath.init(scope, sealed);

    NativeWith.init(scope, sealed);
    NativeCall.init(scope, sealed);
    NativeScript.init(scope, sealed);

    NativeIterator.init(scope, sealed); // Also initializes NativeGenerator

    boolean withXml = cx.hasFeature(Context.FEATURE_E4X)
        && cx.getE4xImplementationFactory() != null;

    for (int i = 0; i != lazilyNames.length; i += 2) {
      String topProperty = lazilyNames[i];
      String className = lazilyNames[i + 1];
      if (!withXml && className.equals("(xml)"))
        continue;
      else if (withXml && className.equals("(xml)"))
        className = cx.getE4xImplementationFactory()
            .getImplementationClassName();
      new LazilyLoadedCtor(scope, topProperty, className, sealed);
    }

    return scope;
  }

  /**
   * The instanceof operator.
   * 
   * @return a instanceof b
   */
  public static boolean instanceOf(Object a, Object b, Context cx) {
    // Check RHS is an object
    if (!(b instanceof Scriptable))
      throw typeError0("msg.instanceof.not.object");

    // for primitive values on LHS, return false
    // XXX we may want to change this so that
    // 5 instanceof Number == true
    if (!(a instanceof Scriptable))
      return false;

    return ((Scriptable) b).hasInstance((Scriptable) a);
  }

  public static boolean isArrayObject(Object obj) {
    return obj instanceof NativeArray || obj instanceof Arguments;
  }

  static boolean isGeneratedScript(String sourceUrl) {
    // ALERT: this may clash with a valid URL containing (eval) or
    // (Function)
    return sourceUrl.indexOf("(eval)") >= 0
        || sourceUrl.indexOf("(Function)") >= 0;
  }

  // It is public so NativeRegExp can access it.
  public static boolean isJSLineTerminator(int c) {
    // Optimization for faster check for eol character:
    // they do not have 0xDFD0 bits set
    if ((c & 0xDFD0) != 0)
      return false;
    return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
  }

  private static boolean isPrimitive(Object obj) {
    return obj instanceof Number || obj instanceof String
        || obj instanceof Boolean;
  }

  public static boolean isRhinoRuntimeType(Class<?> cl) {
    if (cl.isPrimitive())
      return cl != Character.TYPE;
    else
      return cl == StringClass || cl == BooleanClass
          || NumberClass.isAssignableFrom(cl)
          || ScriptableClass.isAssignableFrom(cl);
  }

  static boolean isSpecialProperty(String s) {
    return s.equals("__proto__") || s.equals("__parent__");
  }

  static boolean isValidIdentifierName(String s) {
    int L = s.length();
    if (L == 0)
      return false;
    if (!Character.isJavaIdentifierStart(s.charAt(0)))
      return false;
    for (int i = 1; i != L; ++i)
      if (!Character.isJavaIdentifierPart(s.charAt(i)))
        return false;
    return !TokenStream.isKeyword(s);
  }

  private static boolean isVisible(Context cx, Object obj) {
    ClassShutter shutter = cx.getClassShutter();
    return shutter == null
        || shutter.visibleToScripts(obj.getClass().getName());
  }

  /**
   * Delegates to
   * 
   * @return true iff rhs appears in lhs' proto chain
   */
  public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
    Scriptable proto = lhs.getPrototype();

    while (proto != null) {
      if (proto.equals(rhs))
        return true;
      proto = proto.getPrototype();
    }

    return false;
  }

  static int lastIndexResult(Context cx) {
    return cx.scratchIndex;
  }

  public static Scriptable lastStoredScriptable(Context cx) {
    Scriptable result = cx.scratchScriptable;
    cx.scratchScriptable = null;
    return result;
  }

  public static long lastUint32Result(Context cx) {
    long value = cx.scratchUint32;
    if (value >>> 32 != 0)
      throw new IllegalStateException();
    return value;
  }

  // neg:
  // implement the '-' operator inline in the caller
  // as "-toNumber(val)"

  // not:
  // implement the '!' operator inline in the caller
  // as "!toBoolean(val)"

  // bitnot:
  // implement the '~' operator inline in the caller
  // as "~toInt32(val)"

  public static Scriptable leaveDotQuery(Scriptable scope) {
    NativeWith nw = (NativeWith) scope;
    return nw.getParentScope();
  }

  public static Scriptable leaveWith(Scriptable scope) {
    NativeWith nw = (NativeWith) scope;
    return nw.getParentScope();
  }

  static String makeUrlForGeneratedScript(boolean isEval,
      String masterScriptUrl, int masterScriptLine) {
    if (isEval)
      return masterScriptUrl + '#' + masterScriptLine + "(eval)";
    else
      return masterScriptUrl + '#' + masterScriptLine + "(Function)";
  }

  public static Ref memberRef(Object obj, Object elem, Context cx,
      int memberTypeFlags) {
    if (!(obj instanceof XMLObject))
      throw notXmlError(obj);
    XMLObject xmlObject = (XMLObject) obj;
    return xmlObject.memberRef(cx, elem, memberTypeFlags);
  }

  public static Ref memberRef(Object obj, Object namespace, Object elem,
      Context cx, int memberTypeFlags) {
    if (!(obj instanceof XMLObject))
      throw notXmlError(obj);
    XMLObject xmlObject = (XMLObject) obj;
    return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags);
  }

  /**
   * Looks up a name in the scope chain and returns its value.
   */
  public static Object name(Context cx, Scriptable scope, String name) {
    Scriptable parent = scope.getParentScope();
    if (parent == null) {
      Object result = topScopeName(cx, scope, name);
      if (result == Scriptable.NOT_FOUND)
        throw notFoundError(scope, name);
      return result;
    }

    return nameOrFunction(cx, scope, parent, name, false);
  }

  public static Object nameIncrDecr(Scriptable scopeChain, String id,
      Context cx, int incrDecrMask) {
    Scriptable target;
    Object value;
    search : {
      do {
        if (cx.useDynamicScope && scopeChain.getParentScope() == null)
          scopeChain = checkDynamicScope(cx.topCallScope, scopeChain);
        target = scopeChain;
        do {
          value = target.get(id, scopeChain);
          if (value != Scriptable.NOT_FOUND)
            break search;
          target = target.getPrototype();
        } while (target != null);
        scopeChain = scopeChain.getParentScope();
      } while (scopeChain != null);
      throw notFoundError(scopeChain, id);
    }
    return doScriptableIncrDecr(target, id, scopeChain, value, incrDecrMask);
  }

  /**
   * @deprecated The method is only present for compatibility.
   */
  public static Object nameIncrDecr(Scriptable scopeChain, String id,
      int incrDecrMask) {
    return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask);
  }

  private static Object nameOrFunction(Context cx, Scriptable scope,
      Scriptable parentScope, String name, boolean asFunctionCall) {
    Object result;
    Scriptable thisObj = scope; // It is used only if asFunctionCall==true.

    XMLObject firstXMLObject = null;
    for (;;) {
      if (scope instanceof NativeWith) {
        Scriptable withObj = scope.getPrototype();
        if (withObj instanceof XMLObject) {
          XMLObject xmlObj = (XMLObject) withObj;
          if (xmlObj.ecmaHas(cx, name)) {
            // function this should be the target object of with
            thisObj = xmlObj;
            result = xmlObj.ecmaGet(cx, name);
            break;
          }
          if (firstXMLObject == null)
            firstXMLObject = xmlObj;
        } else {
          result = ScriptableObject.getProperty(withObj, name);
          if (result != Scriptable.NOT_FOUND) {
            // function this should be the target object of with
            thisObj = withObj;
            break;
          }
        }
      } else if (scope instanceof NativeCall) {
        // NativeCall does not prototype chain and Scriptable.get
        // can be called directly.
        result = scope.get(name, scope);
        if (result != Scriptable.NOT_FOUND) {
          if (asFunctionCall)
            // ECMA 262 requires that this for nested funtions
            // should be top scope
            thisObj = ScriptableObject.getTopLevelScope(parentScope);
          break;
        }
      } else {
        // Can happen if Rhino embedding decided that nested
        // scopes are useful for what ever reasons.
        result = ScriptableObject.getProperty(scope, name);
        if (result != Scriptable.NOT_FOUND) {
          thisObj = scope;
          break;
        }
      }
      scope = parentScope;
      parentScope = parentScope.getParentScope();
      if (parentScope == null) {
        result = topScopeName(cx, scope, name);
        if (result == Scriptable.NOT_FOUND) {
          if (firstXMLObject == null || asFunctionCall)
            throw notFoundError(scope, name);
          // The name was not found, but we did find an XML
          // object in the scope chain and we are looking for name,
          // not function. The result should be an empty XMLList
          // in name context.
          result = firstXMLObject.ecmaGet(cx, name);
        }
        // For top scope thisObj for functions is always scope itself.
        thisObj = scope;
        break;
      }
    }

    if (asFunctionCall) {
      if (!(result instanceof Callable))
        throw notFunctionError(result, name);
      storeScriptable(cx, thisObj);
    }

    return result;
  }

  public static Ref nameRef(Object name, Context cx, Scriptable scope,
      int memberTypeFlags) {
    XMLLib xmlLib = currentXMLLib(cx);
    return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
  }

  public static Ref nameRef(Object namespace, Object name, Context cx,
      Scriptable scope, int memberTypeFlags) {
    XMLLib xmlLib = currentXMLLib(cx);
    return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags);
  }

  public static Scriptable newArrayLiteral(Object[] objects, int[] skipIndices,
      Context cx, Scriptable scope) {
    final int SKIP_DENSITY = 2;
    int count = objects.length;
    int skipCount = 0;
    if (skipIndices != null)
      skipCount = skipIndices.length;
    int length = count + skipCount;
    if (length > 1 && skipCount * SKIP_DENSITY < length) {
      // If not too sparse, create whole array for constructor
      Object[] sparse;
      if (skipCount == 0)
        sparse = objects;
      else {
        sparse = new Object[length];
        int skip = 0;
        for (int i = 0, j = 0; i != length; ++i) {
          if (skip != skipCount && skipIndices[skip] == i) {
            sparse[i] = Scriptable.NOT_FOUND;
            ++skip;
            continue;
          }
          sparse[i] = objects[j];
          ++j;
        }
      }
      return cx.newObject(scope, "Array", sparse);
    }

    Scriptable arrayObj = cx.newObject(scope, "Array", ScriptRuntime.emptyArgs);
    int skip = 0;
    for (int i = 0, j = 0; i != length; ++i) {
      if (skip != skipCount && skipIndices[skip] == i) {
        ++skip;
        continue;
      }
      ScriptableObject.putProperty(arrayObj, i, objects[j]);
      ++j;
    }
    return arrayObj;
  }

  public static Scriptable newCatchScope(Throwable t,
      Scriptable lastCatchScope, String exceptionName, Context cx,
      Scriptable scope) {
    Object obj;
    boolean cacheObj;

    getObj : if (t instanceof JavaScriptException) {
      cacheObj = false;
      obj = ((JavaScriptException) t).getValue();
    } else {
      cacheObj = true;

      // Create wrapper object unless it was associated with
      // the previous scope object

      if (lastCatchScope != null) {
        NativeObject last = (NativeObject) lastCatchScope;
        obj = last.getAssociatedValue(t);
        if (obj == null)
          Kit.codeBug();
        break getObj;
      }

      RhinoException re;
      String errorName;
      String errorMsg;
      Throwable javaException = null;

      if (t instanceof EcmaError) {
        EcmaError ee = (EcmaError) t;
        re = ee;
        errorName = ee.getName();
        errorMsg = ee.getErrorMessage();
      } else if (t instanceof WrappedException) {
        WrappedException we = (WrappedException) t;
        re = we;
        javaException = we.getWrappedException();
        errorName = "JavaException";
        errorMsg = javaException.getClass().getName() + ": "
            + javaException.getMessage();
      } else if (t instanceof EvaluatorException) {
        // Pure evaluator exception, nor WrappedException instance
        EvaluatorException ee = (EvaluatorException) t;
        re = ee;
        errorName = "InternalError";
        errorMsg = ee.getMessage();
      } else if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
        // With FEATURE_ENHANCED_JAVA_ACCESS, scripts can catch
        // all exception types
        re = new WrappedException(t);
        errorName = "JavaException";
        errorMsg = t.toString();
      } else
        // Script can catch only instances of JavaScriptException,
        // EcmaError and EvaluatorException
        throw Kit.codeBug();

      String sourceUri = re.sourceName();
      if (sourceUri == null)
        sourceUri = "";
      int line = re.lineNumber();
      Object args[];
      if (line > 0)
        args = new Object[]{errorMsg, sourceUri, new Integer(line)};
      else
        args = new Object[]{errorMsg, sourceUri};

      Scriptable errorObject = cx.newObject(scope, errorName, args);
      ScriptableObject.putProperty(errorObject, "name", errorName);

      if (javaException != null && isVisible(cx, javaException)) {
        Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null);
        ScriptableObject.defineProperty(errorObject, "javaException", wrap,
            ScriptableObject.PERMANENT | ScriptableObject.READONLY);
      }
      if (isVisible(cx, re)) {
        Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
        ScriptableObject.defineProperty(errorObject, "rhinoException", wrap,
            ScriptableObject.PERMANENT | ScriptableObject.READONLY);
      }
      obj = errorObject;
    }

    NativeObject catchScopeObject = new NativeObject();
    // See ECMA 12.4
    catchScopeObject.defineProperty(exceptionName, obj,
        ScriptableObject.PERMANENT);

    if (isVisible(cx, t))
      // Add special Rhino object __exception__ defined in the catch
      // scope that can be used to retrieve the Java exception associated
      // with the JavaScript exception (to get stack trace info, etc.)
      catchScopeObject.defineProperty("__exception__", Context.javaToJS(t,
          scope), ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);

    if (cacheObj)
      catchScopeObject.associateValue(t, obj);
    return catchScopeObject;
  }

  public static Scriptable newObject(Context cx, Scriptable scope,
      String constructorName, Object[] args) {
    scope = ScriptableObject.getTopLevelScope(scope);
    Function ctor = getExistingCtor(cx, scope, constructorName);
    if (args == null)
      args = ScriptRuntime.emptyArgs;
    return ctor.construct(cx, scope, args);
  }
  /**
   * Operator new.
   * 
   * See ECMA 11.2.2
   */
  public static Scriptable newObject(Object fun, Context cx, Scriptable scope,
      Object[] args) {
    if (!(fun instanceof Function))
      throw notFunctionError(fun);
    Function function = (Function) fun;
    return function.construct(cx, scope, args);
  }

  /**
   * This method is here for backward compat with existing compiled code. It is
   * called when an object literal is compiled. The next instance will be the
   * version called from new code.
   * 
   * @deprecated This method only present for compatibility.
   */
  public static Scriptable newObjectLiteral(Object[] propertyIds,
      Object[] propertyValues, Context cx, Scriptable scope) {
    // This will initialize to all zeros, exactly what we need for old-style
    // getterSetters values (no getters or setters in the list)
    int[] getterSetters = new int[propertyIds.length];
    return newObjectLiteral(propertyIds, propertyValues, getterSetters, cx,
        scope);
  }

  public static Scriptable newObjectLiteral(Object[] propertyIds,
      Object[] propertyValues, int[] getterSetters, Context cx, Scriptable scope) {
    Scriptable object = cx.newObject(scope);
    for (int i = 0, end = propertyIds.length; i != end; ++i) {
      Object id = propertyIds[i];
      int getterSetter = getterSetters[i];
      Object value = propertyValues[i];
      if (id instanceof String) {
        if (getterSetter == 0)
          ScriptableObject.putProperty(object, (String) id, value);
        else {
          Callable fun;
          String definer;
          if (getterSetter < 0) // < 0 means get foo() ...
            definer = "__defineGetter__";
          else
            definer = "__defineSetter__";
          fun = getPropFunctionAndThis(object, definer, cx);
          // Must consume the last scriptable object in cx
          lastStoredScriptable(cx);
          Object[] outArgs = new Object[2];
          outArgs[0] = id;
          outArgs[1] = value;
          fun.call(cx, scope, object, outArgs);
        }
      } else {
        int index = ((Integer) id).intValue();
        ScriptableObject.putProperty(object, index, value);
      }
    }
    return object;
  }

  public static Object newSpecial(Context cx, Object fun, Object[] args,
      Scriptable scope, int callType) {
    if (callType == Node.SPECIALCALL_EVAL) {
      if (NativeGlobal.isEvalFunction(fun))
        throw typeError1("msg.not.ctor", "eval");
    } else if (callType == Node.SPECIALCALL_WITH) {
      if (NativeWith.isWithFunction(fun))
        return NativeWith.newWithSpecial(cx, scope, args);
    } else
      throw Kit.codeBug();

    return newObject(fun, cx, scope, args);
  }

  public static RuntimeException notFoundError(Scriptable object,
      String property) {
    // XXX: use object to improve the error message
    String msg = getMessage1("msg.is.not.defined", property);
    throw constructError("ReferenceError", msg);
  }

  public static RuntimeException notFunctionError(Object value) {
    return notFunctionError(value, value);
  }

  // ------------------
  // Statements
  // ------------------

  public static RuntimeException notFunctionError(Object value,
      Object messageHelper) {
    // Use value for better error reporting
    String msg = messageHelper == null ? "null" : messageHelper.toString();
    if (value == Scriptable.NOT_FOUND)
      return typeError1("msg.function.not.found", msg);
    return typeError2("msg.isnt.function", msg, typeof(value));
  }

  public static RuntimeException notFunctionError(Object obj, Object value,
      String propertyName) {
    // Use obj and value for better error reporting
    String objString = toString(obj);
    if (value == Scriptable.NOT_FOUND)
      return typeError2("msg.function.not.found.in", propertyName, objString);
    return typeError3("msg.isnt.function.in", propertyName, objString,
        typeof(value));
  }

  private static RuntimeException notXmlError(Object value) {
    throw typeError1("msg.isnt.xml.object", toString(value));
  }

  public static String numberToString(double d, int base) {
    if (d != d)
      return "NaN";
    if (d == Double.POSITIVE_INFINITY)
      return "Infinity";
    if (d == Double.NEGATIVE_INFINITY)
      return "-Infinity";
    if (d == 0.0)
      return "0";

    if (base < 2 || base > 36)
      throw Context
          .reportRuntimeError1("msg.bad.radix", Integer.toString(base));

    if (base != 10)
      return DToA.JS_dtobasestr(base, d);
    else {
      StringBuffer result = new StringBuffer();
      DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
      return result.toString();
    }

  }

  /**
   * Helper function for builtin objects that use the varargs form. ECMA
   * function formal arguments are undefined if not supplied; this function pads
   * the argument array out to the expected length, if necessary.
   */
  public static Object[] padArguments(Object[] args, int count) {
    if (count < args.length)
      return args;

    int i;
    Object[] result = new Object[count];
    for (i = 0; i < args.length; i++)
      result[i] = args[i];

    for (; i < count; i++)
      result[i] = Undefined.instance;

    return result;
  }

  public static Object propIncrDecr(Object obj, String id, Context cx,
      int incrDecrMask) {
    Scriptable start = toObjectOrNull(cx, obj);
    if (start == null)
      throw undefReadError(obj, id);

    Scriptable target = start;
    Object value;
    search : {
      do {
        value = target.get(id, start);
        if (value != Scriptable.NOT_FOUND)
          break search;
        target = target.getPrototype();
      } while (target != null);
      start.put(id, start, NaNobj);
      return NaNobj;
    }
    return doScriptableIncrDecr(target, id, start, value, incrDecrMask);
  }

  public static Object refDel(Ref ref, Context cx) {
    return wrapBoolean(ref.delete(cx));
  }

  public static Object refGet(Ref ref, Context cx) {
    return ref.get(cx);
  }

  public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) {
    Object value = ref.get(cx);
    boolean post = (incrDecrMask & Node.POST_FLAG) != 0;
    double number;
    if (value instanceof Number)
      number = ((Number) value).doubleValue();
    else {
      number = toNumber(value);
      if (post)
        // convert result to number
        value = wrapNumber(number);
    }
    if ((incrDecrMask & Node.DECR_FLAG) == 0)
      ++number;
    else
      --number;
    Number result = wrapNumber(number);
    ref.set(cx, result);
    if (post)
      return value;
    else
      return result;
  }

  public static Object refSet(Ref ref, Object value, Context cx) {
    return ref.set(cx, value);
  }

  public static Object searchDefaultNamespace(Context cx) {
    Scriptable scope = cx.currentActivationCall;
    if (scope == null)
      scope = getTopCallScope(cx);
    Object nsObject;
    for (;;) {
      Scriptable parent = scope.getParentScope();
      if (parent == null) {
        nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG);
        if (nsObject == Scriptable.NOT_FOUND)
          return null;
        break;
      }
      nsObject = scope.get(DEFAULT_NS_TAG, scope);
      if (nsObject != Scriptable.NOT_FOUND)
        break;
      scope = parent;
    }
    return nsObject;
  }

  public static Object setConst(Scriptable bound, Object value, Context cx,
      String id) {
    if (bound instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) bound;
      xmlObject.ecmaPut(cx, id, value);
    } else
      ScriptableObject.putConstProperty(bound, id, value);
    return value;
  }

  public static Object setDefaultNamespace(Object namespace, Context cx) {
    Scriptable scope = cx.currentActivationCall;
    if (scope == null)
      scope = getTopCallScope(cx);

    XMLLib xmlLib = currentXMLLib(cx);
    Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);

    // XXX : this should be in separated namesapce from Scriptable.get/put
    if (!scope.has(DEFAULT_NS_TAG, scope))
      // XXX: this is racy of cause
      ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
          ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
    else
      scope.put(DEFAULT_NS_TAG, scope, ns);

    return Undefined.instance;
  }

  public static void setEnumNumbers(Object enumObj, boolean enumNumbers) {
    ((IdEnumeration) enumObj).enumNumbers = enumNumbers;
  }

  public static void setFunctionProtoAndParent(BaseFunction fn, Scriptable scope) {
    fn.setParentScope(scope);
    fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
  }

  public static Object setName(Scriptable bound, Object value, Context cx,
      Scriptable scope, String id) {
    if (bound != null) {
      if (bound instanceof XMLObject) {
        XMLObject xmlObject = (XMLObject) bound;
        xmlObject.ecmaPut(cx, id, value);
      } else
        ScriptableObject.putProperty(bound, id, value);
    } else {
      // "newname = 7;", where 'newname' has not yet
      // been defined, creates a new property in the
      // top scope unless strict mode is specified.
      if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
          || cx.hasFeature(Context.FEATURE_STRICT_VARS))
        Context.reportWarning(ScriptRuntime.getMessage1(
            "msg.assn.create.strict", id));
      // Find the top scope by walking up the scope chain.
      bound = ScriptableObject.getTopLevelScope(scope);
      if (cx.useDynamicScope)
        bound = checkDynamicScope(cx.topCallScope, bound);
      bound.put(id, bound, value);
    }
    return value;
  }

  /*
   * Call obj.[[Put]](id, value)
   */
  public static Object setObjectElem(Object obj, Object elem, Object value,
      Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefWriteError(obj, elem, value);
    return setObjectElem(sobj, elem, value, cx);
  }

  public static Object setObjectElem(Scriptable obj, Object elem, Object value,
      Context cx) {
    if (obj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) obj;
      xmlObject.ecmaPut(cx, elem, value);
      return value;
    }

    String s = toStringIdOrIndex(cx, elem);
    if (s == null) {
      int index = lastIndexResult(cx);
      ScriptableObject.putProperty(obj, index, value);
    } else
      ScriptableObject.putProperty(obj, s, value);

    return value;
  }

  /*
   * A cheaper and less general version of the above for well-known argument
   * types.
   */
  public static Object setObjectIndex(Object obj, double dblIndex,
      Object value, Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefWriteError(obj, String.valueOf(dblIndex), value);

    int index = (int) dblIndex;
    if (index == dblIndex)
      return setObjectIndex(sobj, index, value, cx);
    else {
      String s = toString(dblIndex);
      return setObjectProp(sobj, s, value, cx);
    }
  }

  public static Object setObjectIndex(Scriptable obj, int index, Object value,
      Context cx) {
    if (obj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) obj;
      xmlObject.ecmaPut(cx, new Integer(index), value);
    } else
      ScriptableObject.putProperty(obj, index, value);
    return value;
  }

  /**
   * Version of setObjectElem when elem is a valid JS identifier name.
   */
  public static Object setObjectProp(Object obj, String property, Object value,
      Context cx) {
    Scriptable sobj = toObjectOrNull(cx, obj);
    if (sobj == null)
      throw undefWriteError(obj, property, value);
    return setObjectProp(sobj, property, value, cx);
  }

  public static Object setObjectProp(Scriptable obj, String property,
      Object value, Context cx) {
    if (obj instanceof XMLObject) {
      XMLObject xmlObject = (XMLObject) obj;
      xmlObject.ecmaPut(cx, property, value);
    } else
      ScriptableObject.putProperty(obj, property, value);
    return value;
  }

  public static void setObjectProtoAndParent(ScriptableObject object,
      Scriptable scope) {
    // Compared with function it always sets the scope to top scope
    scope = ScriptableObject.getTopLevelScope(scope);
    object.setParentScope(scope);
    Scriptable proto = ScriptableObject.getClassPrototype(scope, object
        .getClassName());
    object.setPrototype(proto);
  }

  public static void setRegExpProxy(Context cx, RegExpProxy proxy) {
    if (proxy == null)
      throw new IllegalArgumentException();
    cx.regExpProxy = proxy;
  }

  public static boolean shallowEq(Object x, Object y) {
    if (x == y) {
      if (!(x instanceof Number))
        return true;
      // NaN check
      double d = ((Number) x).doubleValue();
      return d == d;
    }
    if (x == null || x == Undefined.instance)
      return false;
    else if (x instanceof Number) {
      if (y instanceof Number)
        return ((Number) x).doubleValue() == ((Number) y).doubleValue();
    } else if (x instanceof String) {
      if (y instanceof String)
        return x.equals(y);
    } else if (x instanceof Boolean) {
      if (y instanceof Boolean)
        return x.equals(y);
    } else if (x instanceof Scriptable) {
      if (x instanceof Wrapper && y instanceof Wrapper)
        return ((Wrapper) x).unwrap() == ((Wrapper) y).unwrap();
    } else {
      warnAboutNonJSObject(x);
      return x == y;
    }
    return false;
  }

  public static Ref specialRef(Object obj, String specialProperty, Context cx) {
    return SpecialRef.createSpecial(cx, obj, specialProperty);
  }

  private static void storeIndexResult(Context cx, int index) {
    cx.scratchIndex = index;
  }

  private static void storeScriptable(Context cx, Scriptable value) {
    // The previously stored scratchScriptable should be consumed
    if (cx.scratchScriptable != null)
      throw new IllegalStateException();
    cx.scratchScriptable = value;
  }

  public static void storeUint32Result(Context cx, long value) {
    if (value >>> 32 != 0)
      throw new IllegalArgumentException();
    cx.scratchUint32 = value;
  }

  /*
   * Helper function for toNumber, parseInt, and TokenStream.getToken.
   */
  static double stringToNumber(String s, int start, int radix) {
    char digitMax = '9';
    char lowerCaseBound = 'a';
    char upperCaseBound = 'A';
    int len = s.length();
    if (radix < 10)
      digitMax = (char) ('0' + radix - 1);
    if (radix > 10) {
      lowerCaseBound = (char) ('a' + radix - 10);
      upperCaseBound = (char) ('A' + radix - 10);
    }
    int end;
    double sum = 0.0;
    for (end = start; end < len; end++) {
      char c = s.charAt(end);
      int newDigit;
      if ('0' <= c && c <= digitMax)
        newDigit = c - '0';
      else if ('a' <= c && c < lowerCaseBound)
        newDigit = c - 'a' + 10;
      else if ('A' <= c && c < upperCaseBound)
        newDigit = c - 'A' + 10;
      else
        break;
      sum = sum * radix + newDigit;
    }
    if (start == end)
      return NaN;
    if (sum >= 9007199254740992.0)
      if (radix == 10)
        /*
         * If we're accumulating a decimal number and the number is >= 2^53,
         * then the result from the repeated multiply-add above may be
         * inaccurate. Call Java to get the correct answer.
         */
        try {
          return Double.valueOf(s.substring(start, end)).doubleValue();
        } catch (NumberFormatException nfe) {
          return NaN;
        }
      else if (radix == 2 || radix == 4 || radix == 8 || radix == 16
          || radix == 32) {
        /*
         * The number may also be inaccurate for one of these bases. This
         * happens if the addition in value*radix + digit causes a round-down to
         * an even least significant mantissa bit when the first dropped bit is
         * a one. If any of the following digits in the number (which haven't
         * been added in yet) are nonzero then the correct action would have
         * been to round up instead of down. An example of this occurs when
         * reading the number 0x1000000000000081, which rounds to
         * 0x1000000000000000 instead of 0x1000000000000100.
         */
        int bitShiftInChar = 1;
        int digit = 0;

        final int SKIP_LEADING_ZEROS = 0;
        final int FIRST_EXACT_53_BITS = 1;
        final int AFTER_BIT_53 = 2;
        final int ZEROS_AFTER_54 = 3;
        final int MIXED_AFTER_54 = 4;

        int state = SKIP_LEADING_ZEROS;
        int exactBitsLimit = 53;
        double factor = 0.0;
        boolean bit53 = false;
        // bit54 is the 54th bit (the first dropped from the mantissa)
        boolean bit54 = false;

        for (;;) {
          if (bitShiftInChar == 1) {
            if (start == end)
              break;
            digit = s.charAt(start++);
            if ('0' <= digit && digit <= '9')
              digit -= '0';
            else if ('a' <= digit && digit <= 'z')
              digit -= 'a' - 10;
            else
              digit -= 'A' - 10;
            bitShiftInChar = radix;
          }
          bitShiftInChar >>= 1;
          boolean bit = (digit & bitShiftInChar) != 0;

          switch (state) {
            case SKIP_LEADING_ZEROS :
              if (bit) {
                --exactBitsLimit;
                sum = 1.0;
                state = FIRST_EXACT_53_BITS;
              }
              break;
            case FIRST_EXACT_53_BITS :
              sum *= 2.0;
              if (bit)
                sum += 1.0;
              --exactBitsLimit;
              if (exactBitsLimit == 0) {
                bit53 = bit;
                state = AFTER_BIT_53;
              }
              break;
            case AFTER_BIT_53 :
              bit54 = bit;
              factor = 2.0;
              state = ZEROS_AFTER_54;
              break;
            case ZEROS_AFTER_54 :
              if (bit)
                state = MIXED_AFTER_54;
              // fallthrough
            case MIXED_AFTER_54 :
              factor *= 2;
              break;
          }
        }
        switch (state) {
          case SKIP_LEADING_ZEROS :
            sum = 0.0;
            break;
          case FIRST_EXACT_53_BITS :
          case AFTER_BIT_53 :
            // do nothing
            break;
          case ZEROS_AFTER_54 :
            // x1.1 -> x1 + 1 (round up)
            // x0.1 -> x0 (round down)
            if (bit54 & bit53)
              sum += 1.0;
            sum *= factor;
            break;
          case MIXED_AFTER_54 :
            // x.100...1.. -> x + 1 (round up)
            // x.0anything -> x (round down)
            if (bit54)
              sum += 1.0;
            sum *= factor;
            break;
        }
      }
    /* We don't worry about inaccurate numbers for any other base. */
    return sum;
  }

  /**
   * If str is a decimal presentation of Uint32 value, return it as long.
   * Othewise return -1L;
   */
  public static long testUint32String(String str) {
    // The length of the decimal string representation of
    // UINT32_MAX_VALUE, 4294967296
    final int MAX_VALUE_LENGTH = 10;

    int len = str.length();
    if (1 <= len && len <= MAX_VALUE_LENGTH) {
      int c = str.charAt(0);
      c -= '0';
      if (c == 0)
        // Note that 00,01 etc. are not valid Uint32 presentations
        return len == 1 ? 0L : -1L;
      if (1 <= c && c <= 9) {
        long v = c;
        for (int i = 1; i != len; ++i) {
          c = str.charAt(i) - '0';
          if (!(0 <= c && c <= 9))
            return -1;
          v = 10 * v + c;
        }
        // Check for overflow
        if (v >>> 32 == 0)
          return v;
      }
    }
    return -1;
  }

  /**
   * Convert the value to a boolean.
   * 
   * See ECMA 9.2.
   */
  public static boolean toBoolean(Object val) {
    for (;;) {
      if (val instanceof Boolean)
        return ((Boolean) val).booleanValue();
      if (val == null || val == Undefined.instance)
        return false;
      if (val instanceof String)
        return ((String) val).length() != 0;
      if (val instanceof Number) {
        double d = ((Number) val).doubleValue();
        return d == d && d != 0.0;
      }
      if (val instanceof Scriptable) {
        if (val instanceof ScriptableObject
            && ((ScriptableObject) val).avoidObjectDetection())
          return false;
        if (Context.getContext().isVersionECMA1())
          // pure ECMA
          return true;
        // ECMA extension
        val = ((Scriptable) val).getDefaultValue(BooleanClass);
        if (val instanceof Scriptable)
          throw errorWithClassName("msg.primitive.expected", val);
        continue;
      }
      warnAboutNonJSObject(val);
      return true;
    }
  }

  public static int toInt32(double d) {
    int id = (int) d;
    if (id == d)
      // This covers -0.0 as well
      return id;

    if (d != d || d == Double.POSITIVE_INFINITY
        || d == Double.NEGATIVE_INFINITY)
      return 0;

    d = d >= 0 ? Math.floor(d) : Math.ceil(d);

    double two32 = 4294967296.0;
    d = Math.IEEEremainder(d, two32);
    // (double)(long)d == d should hold here

    long l = (long) d;
    // returning (int)d does not work as d can be outside int range
    // but the result must always be 32 lower bits of l
    return (int) l;
  }

  /**
   * 
   * See ECMA 9.5.
   */
  public static int toInt32(Object val) {
    // short circuit for common integer values
    if (val instanceof Integer)
      return ((Integer) val).intValue();

    return toInt32(toNumber(val));
  }

  public static int toInt32(Object[] args, int index) {
    return index < args.length ? toInt32(args[index]) : 0;
  }

  // convenience method
  public static double toInteger(final double d) {
    // if it's NaN
    if (d != d)
      return +0.0;

    if (d == 0.0 || d == Double.POSITIVE_INFINITY
        || d == Double.NEGATIVE_INFINITY)
      return d;

    if (d > 0.0)
      return Math.floor(d);
    else
      return Math.ceil(d);
  }

  /**
   * 
   * See ECMA 9.4.
   */
  public static double toInteger(Object val) {
    return toInteger(toNumber(val));
  }

  public static double toInteger(Object[] args, int index) {
    return index < args.length ? toInteger(args[index]) : +0.0;
  }

  public static Scriptable toIterator(Context cx, Scriptable scope,
      Scriptable obj, boolean keyOnly) {
    if (ScriptableObject
        .hasProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME)) {
      Object v = ScriptableObject.getProperty(obj,
          NativeIterator.ITERATOR_PROPERTY_NAME);
      if (!(v instanceof Callable))
        throw typeError0("msg.invalid.iterator");
      Callable f = (Callable) v;
      Object[] args = new Object[]{keyOnly ? Boolean.TRUE : Boolean.FALSE};
      v = f.call(cx, scope, obj, args);
      if (!(v instanceof Scriptable))
        throw typeError0("msg.iterator.primitive");
      return (Scriptable) v;
    }
    return null;
  }

  /**
   * Convert the value to a number.
   * 
   * See ECMA 9.3.
   */
  public static double toNumber(Object val) {
    for (;;) {
      if (val instanceof Number)
        return ((Number) val).doubleValue();
      if (val == null)
        return +0.0;
      if (val == Undefined.instance)
        return NaN;
      if (val instanceof String)
        return toNumber((String) val);
      if (val instanceof Boolean)
        return ((Boolean) val).booleanValue() ? 1 : +0.0;
      if (val instanceof Scriptable) {
        val = ((Scriptable) val).getDefaultValue(NumberClass);
        if (val instanceof Scriptable)
          throw errorWithClassName("msg.primitive.expected", val);
        continue;
      }
      warnAboutNonJSObject(val);
      return NaN;
    }
  }

  public static double toNumber(Object[] args, int index) {
    return index < args.length ? toNumber(args[index]) : NaN;
  }

  /**
   * ToNumber applied to the String type
   * 
   * See ECMA 9.3.1
   */
  public static double toNumber(String s) {
    int len = s.length();
    int start = 0;
    char startChar;
    for (;;) {
      if (start == len)
        // Empty or contains only whitespace
        return +0.0;
      startChar = s.charAt(start);
      if (!Character.isWhitespace(startChar))
        break;
      start++;
    }

    if (startChar == '0') {
      if (start + 2 < len) {
        int c1 = s.charAt(start + 1);
        if (c1 == 'x' || c1 == 'X')
          // A hexadecimal number
          return stringToNumber(s, start + 2, 16);
      }
    } else if (startChar == '+' || startChar == '-')
      if (start + 3 < len && s.charAt(start + 1) == '0') {
        int c2 = s.charAt(start + 2);
        if (c2 == 'x' || c2 == 'X') {
          // A hexadecimal number with sign
          double val = stringToNumber(s, start + 3, 16);
          return startChar == '-' ? -val : val;
        }
      }

    int end = len - 1;
    char endChar;
    while (Character.isWhitespace(endChar = s.charAt(end)))
      end--;
    if (endChar == 'y') {
      // check for "Infinity"
      if (startChar == '+' || startChar == '-')
        start++;
      if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
        return startChar == '-'
            ? Double.NEGATIVE_INFINITY
            : Double.POSITIVE_INFINITY;
      return NaN;
    }
    // A non-hexadecimal, non-infinity number:
    // just try a normal floating point conversion
    String sub = s.substring(start, end + 1);
    if (MSJVM_BUG_WORKAROUNDS)
      // The MS JVM will accept non-conformant strings
      // rather than throwing a NumberFormatException
      // as it should.
      for (int i = sub.length() - 1; i >= 0; i--) {
        char c = sub.charAt(i);
        if ('0' <= c && c <= '9' || c == '.' || c == 'e' || c == 'E'
            || c == '+' || c == '-')
          continue;
        return NaN;
      }
    try {
      return Double.valueOf(sub).doubleValue();
    } catch (NumberFormatException ex) {
      return NaN;
    }
  }

  /**
   * Convert the value to an object.
   * 
   * See ECMA 9.9.
   */
  public static Scriptable toObject(Context cx, Scriptable scope, Object val) {
    if (val instanceof Scriptable)
      return (Scriptable) val;
    if (val == null)
      throw typeError0("msg.null.to.object");
    if (val == Undefined.instance)
      throw typeError0("msg.undef.to.object");
    String className = val instanceof String ? "String" : val instanceof Number
        ? "Number"
        : val instanceof Boolean ? "Boolean" : null;
    if (className != null) {
      Object[] args = {val};
      scope = ScriptableObject.getTopLevelScope(scope);
      return newObject(cx, scope, className, args);
    }

    // Extension: Wrap as a LiveConnect object.
    Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
    if (wrapped instanceof Scriptable)
      return (Scriptable) wrapped;
    throw errorWithClassName("msg.invalid.type", val);
  }

  /**
   * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
   */
  @Deprecated
  public static Scriptable toObject(Context cx, Scriptable scope, Object val,
      Class<?> staticClass) {
    return toObject(cx, scope, val);
  }

  public static Scriptable toObject(Scriptable scope, Object val) {
    if (val instanceof Scriptable)
      return (Scriptable) val;
    return toObject(Context.getContext(), scope, val);
  }

  /**
   * @deprecated Use {@link #toObject(Scriptable, Object)} instead.
   */
  @Deprecated
  public static Scriptable toObject(Scriptable scope, Object val,
      Class<?> staticClass) {
    if (val instanceof Scriptable)
      return (Scriptable) val;
    return toObject(Context.getContext(), scope, val);
  }

  /**
   * Warning: this doesn't allow to resolve primitive prototype properly when
   * many top scopes are involved
   */
  public static Scriptable toObjectOrNull(Context cx, Object obj) {
    if (obj instanceof Scriptable)
      return (Scriptable) obj;
    else if (obj != null && obj != Undefined.instance)
      return toObject(cx, getTopCallScope(cx), obj);
    return null;
  }

  /**
   * @param scope
   *          the scope that should be used to resolve primitive prototype
   */
  public static Scriptable toObjectOrNull(Context cx, Object obj,
      final Scriptable scope) {
    if (obj instanceof Scriptable)
      return (Scriptable) obj;
    else if (obj != null && obj != Undefined.instance)
      return toObject(cx, scope, obj);
    return null;
  }

  private static Object toPrimitive(Object val) {
    if (!(val instanceof Scriptable))
      return val;
    Scriptable s = (Scriptable) val;
    Object result = s.getDefaultValue(null);
    if (result instanceof Scriptable)
      throw typeError0("msg.bad.default.value");
    return result;
  }

  private static Object topScopeName(Context cx, Scriptable scope, String name) {
    if (cx.useDynamicScope)
      scope = checkDynamicScope(cx.topCallScope, scope);
    return ScriptableObject.getProperty(scope, name);
  }

  /**
   * Optimized version of toString(Object) for numbers.
   */
  public static String toString(double val) {
    return numberToString(val, 10);
  }

  /**
   * Convert the value to a string.
   * 
   * See ECMA 9.8.
   */
  public static String toString(Object val) {
    for (;;) {
      if (val == null)
        return "null";
      if (val == Undefined.instance)
        return "undefined";
      if (val instanceof String)
        return (String) val;
      if (val instanceof Number)
        // XXX should we just teach NativeNumber.stringValue()
        // about Numbers?
        return numberToString(((Number) val).doubleValue(), 10);
      if (val instanceof Scriptable) {
        val = ((Scriptable) val).getDefaultValue(StringClass);
        if (val instanceof Scriptable)
          throw errorWithClassName("msg.primitive.expected", val);
        continue;
      }
      return val.toString();
    }
  }

  public static String toString(Object[] args, int index) {
    return index < args.length ? toString(args[index]) : "undefined";
  }

  /**
   * If toString(id) is a decimal presentation of int32 value, then id is index.
   * In this case return null and make the index available as
   * ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
   */
  static String toStringIdOrIndex(Context cx, Object id) {
    if (id instanceof Number) {
      double d = ((Number) id).doubleValue();
      int index = (int) d;
      if (index == d) {
        storeIndexResult(cx, index);
        return null;
      }
      return toString(id);
    } else {
      String s;
      if (id instanceof String)
        s = (String) id;
      else
        s = toString(id);
      long indexTest = indexFromString(s);
      if (indexTest >= 0) {
        storeIndexResult(cx, (int) indexTest);
        return null;
      }
      return s;
    }
  }

  /**
   * 
   * See ECMA 9.7.
   */
  public static char toUint16(Object val) {
    double d = toNumber(val);

    int i = (int) d;
    if (i == d)
      return (char) i;

    if (d != d || d == Double.POSITIVE_INFINITY
        || d == Double.NEGATIVE_INFINITY)
      return 0;

    d = d >= 0 ? Math.floor(d) : Math.ceil(d);

    int int16 = 0x10000;
    i = (int) Math.IEEEremainder(d, int16);

    return (char) i;
  }

  /**
   * See ECMA 9.6.
   * 
   * @return long value representing 32 bits unsigned integer
   */
  public static long toUint32(double d) {
    long l = (long) d;
    if (l == d)
      // This covers -0.0 as well
      return l & 0xffffffffL;

    if (d != d || d == Double.POSITIVE_INFINITY
        || d == Double.NEGATIVE_INFINITY)
      return 0;

    d = d >= 0 ? Math.floor(d) : Math.ceil(d);

    // 0x100000000 gives me a numeric overflow...
    double two32 = 4294967296.0;
    l = (long) Math.IEEEremainder(d, two32);

    return l & 0xffffffffL;
  }

  public static long toUint32(Object val) {
    return toUint32(toNumber(val));
  }

  public static EcmaError typeError(String message) {
    return constructError("TypeError", message);
  }

  public static EcmaError typeError0(String messageId) {
    String msg = getMessage0(messageId);
    return typeError(msg);
  }

  public static EcmaError typeError1(String messageId, String arg1) {
    String msg = getMessage1(messageId, arg1);
    return typeError(msg);
  }

  public static EcmaError typeError2(String messageId, String arg1, String arg2) {
    String msg = getMessage2(messageId, arg1, arg2);
    return typeError(msg);
  }

  public static EcmaError typeError3(String messageId, String arg1,
      String arg2, String arg3) {
    String msg = getMessage3(messageId, arg1, arg2, arg3);
    return typeError(msg);
  }

  /**
   * The typeof operator
   */
  public static String typeof(Object value) {
    if (value == null)
      return "object";
    if (value == Undefined.instance)
      return "undefined";
    if (value instanceof Scriptable) {
      if (value instanceof ScriptableObject
          && ((ScriptableObject) value).avoidObjectDetection())
        return "undefined";
      if (value instanceof XMLObject)
        return "xml";
      return value instanceof Callable ? "function" : "object";
    }
    if (value instanceof String)
      return "string";
    if (value instanceof Number)
      return "number";
    if (value instanceof Boolean)
      return "boolean";
    throw errorWithClassName("msg.invalid.type", value);
  }

  /**
   * The typeof operator that correctly handles the undefined case
   */
  public static String typeofName(Scriptable scope, String id) {
    Context cx = Context.getContext();
    Scriptable val = bind(cx, scope, id);
    if (val == null)
      return "undefined";
    return typeof(getObjectProp(val, id, cx));
  }

  public static RuntimeException undefCallError(Object object, Object id) {
    String idStr = id == null ? "null" : id.toString();
    return typeError2("msg.undef.method.call", toString(object), idStr);
  }

  public static RuntimeException undefReadError(Object object, Object id) {
    String idStr = id == null ? "null" : id.toString();
    return typeError2("msg.undef.prop.read", toString(object), idStr);
  }

  public static RuntimeException undefWriteError(Object object, Object id,
      Object value) {
    String idStr = id == null ? "null" : id.toString();
    String valueStr = value instanceof Scriptable
        ? value.toString()
        : toString(value);
    return typeError3("msg.undef.prop.write", toString(object), idStr, valueStr);
  }

  static String uneval(Context cx, Scriptable scope, Object value) {
    if (value == null)
      return "null";
    if (value == Undefined.instance)
      return "undefined";
    if (value instanceof String) {
      String escaped = escapeString((String) value);
      StringBuffer sb = new StringBuffer(escaped.length() + 2);
      sb.append('\"');
      sb.append(escaped);
      sb.append('\"');
      return sb.toString();
    }
    if (value instanceof Number) {
      double d = ((Number) value).doubleValue();
      if (d == 0 && 1 / d < 0)
        return "-0";
      return toString(d);
    }
    if (value instanceof Boolean)
      return toString(value);
    if (value instanceof Scriptable) {
      Scriptable obj = (Scriptable) value;
      // Wrapped Java objects won't have "toSource" and will report
      // errors for get()s of nonexistent name, so use has() first
      if (ScriptableObject.hasProperty(obj, "toSource")) {
        Object v = ScriptableObject.getProperty(obj, "toSource");
        if (v instanceof Function) {
          Function f = (Function) v;
          return toString(f.call(cx, scope, obj, emptyArgs));
        }
      }
      return toString(value);
    }
    warnAboutNonJSObject(value);
    return value.toString();
  }

  public static Object updateDotQuery(boolean value, Scriptable scope) {
    // Return null to continue looping
    NativeWith nw = (NativeWith) scope;
    return nw.updateDotQuery(value);
  }

  private static void warnAboutNonJSObject(Object nonJSObject) {
    String message = "RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n"
        + "Rhino runtime detected object "
        + nonJSObject
        + " of class "
        + nonJSObject.getClass().getName()
        + " where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call.";
    Context.reportWarning(message);
    // Just to be sure that it would be noticed
    System.err.println(message);
  }

  public static Boolean wrapBoolean(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
  }

  public static Integer wrapInt(int i) {
    return new Integer(i);
  }

  public static Number wrapNumber(double x) {
    if (x != x)
      return ScriptRuntime.NaNobj;
    return new Double(x);
  }
  /**
   * No instances should be created.
   */
  protected ScriptRuntime() {
  }

}
